Codeforce Round #547 div3
A.Game 23
1.题目
A
2.思路
直接考虑dfs即可,记录最小值,最后输出即可
#include <stdio.h>
const int maxn=1e9+7;
int n,m;
int min=maxn;
void dfs(int n,int tar,int step)
{
if(n>tar)
{
return;
}
if(n==tar)
{
min=min<step?min:step;
}
dfs(n*2,tar,step+1);
dfs(n*3,tar,step+1);
}
int main()
{
scanf("%d%d",&n,&m);
if(n==m)
{
printf("0\n");
}
else
{
dfs(n,m,0);
if(min!=maxn)
printf("%d\n",min);
else
printf("-1\n");
}
return 0;
}#include <stdio.h>
const int maxn=1e9+7;
int n,m;
int min=maxn;
void dfs(int n,int tar,int step)
{
if(n>tar)
{
return;
}
if(n==tar)
{
min=min<step?min:step;
}
dfs(n*2,tar,step+1);
dfs(n*3,tar,step+1);
}
int main()
{
scanf("%d%d",&n,&m);
if(n==m)
{
printf("0\n");
}
else
{
dfs(n,m,0);
if(min!=maxn)
printf("%d\n",min);
else
printf("-1\n");
}
return 0;
}
B.Maximal Continuous Rest
1.题目
B
2.解答
问题看起来有点像循环数组,求最大连续字段的最大长度,只要加一个%n循环求最大长度即可。
#include <stdio.h>
const int maxn=2*1e5+7;
int n;
int time[maxn];
int max=-1;
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&time[i]);
}
int count=1;
int i;
for(i=0;i<n;i++)
{
if(time[i])
break;
}
if(i==n)
{
printf("0\n");
return 0;
}
for(i=0;i<2*n;i++)
{
if(time[i%n]&&time[(i+1)%n])
{
count++;
}
else
{
max=max>count?max:count;
count=1;
}
}
max=max>count?max:count;
printf("%d\n",max);
return 0;
}
C.Polycarp Restores Permutation
1.题目
C
2.解答
我们这样考虑,假设原来的序列为a,b,c,d…h.
现在我们有b-a,c-b,d-c…,那么现在我们将两两之间的差相加起来
会得到b-a,c-a,d-a,e-a,f-a…我们假设其中任意一个元素xi为1,那么我们可以知道,在xi的位置此时的和是最小的,由此我们可以找到1的位置。任何一个属于[1,n]的元素-a,不会比1-a更小(十分显然),找到一的位置,剩下的东西就十分好判断了
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
int n;
const int maxn=2*1e5+7;
long long int q[maxn];
long long int sum[maxn];
int main()
{
scanf("%d",&n);
long long int low=99999999;
memset(sum,0,sizeof(sum));
int b[maxn]={0};
for(int i=1;i<=n-1;i++)
{
scanf("%lld",&q[i]);
sum[i]=sum[i-1]+q[i];
}
for(int i=0;i<=n-1;i++)
{
low=min(low,sum[i]);//寻找一
}
for(int i=0;i<n;i++)
{
if(sum[i]-low+1>n||b[sum[i]-low]>=1)
{
//不满足情况的点有数值大于n
//或者中间出现0
printf("-1\n");
return 0;
}
b[sum[i]-low]=1;
}
for(int i=0;i<n;i++)
{
printf("%lld ",sum[i]-low+1);
}
puts("");
return 0;
}
D.Colored Boots
1.题目
D
2.解答
首先我们先对两个字符数组进行排序,将’?’放在数组的最后边
然后我们分别从两个字符数组的开头开始比较,此处相当于寻找字串。寻找字串完毕后,剩下的元素就是互相不匹配的和’?’,那么之后我们从第一个数组的末尾开始,将‘?’与第二个数组从头开始剩下的元素进行匹配,再对第二个数组进行同样的操作,即可
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;
const int maxn=15*1e4+7;
vector<pair<int,int> >ans;
struct node
{
char c;
int num;
};
node d1[maxn];
node d2[maxn];
node s1[maxn];
node s2[maxn];
bool cmp(node a,node b)
{
if(a.c=='?')
{
return false;
}
if(b.c=='?')
{
return true;
}
return a.c<b.c;
}//sort
void read(int n,node *a)
{
char c;
for(int i=1;i<=n;i++)
{
c=getchar();
a[i].c=c;
a[i].num=i;
}
return;
}
int main()
{
int n;
scanf("%d",&n);
getchar();
char c;
ans=vector<pair<int,int> >(n);
read(n,d1);
getchar();
read(n,d2);
sort(d1+1,d1+1+n,cmp);
sort(d2+1,d2+1+n,cmp);
int c1=0;
int c2=0;
int i=1;
int j=1;
ans.clear();
while(1)
{
if(i>n||j>n)
{
break;
}
if(d1[i].c==d2[j].c)
{
ans.push_back(make_pair(d1[i].num,d2[j].num));
i++;
j++;
}
else if(d1[i].c>d2[j].c)
{
s2[c2]=d2[j];//存入d2没有匹配的元素
c2++;
j++;
}
else if(d1[i].c<d2[j].c)
{
s1[c1]=d1[i];//存入d1没有匹配的元素
c1++;
i++;
}
}
for(i;i<=n;i++)
{
s1[c1]=d1[i];//放入剩下的元素
c1++;
}
for(j;j<=n;j++)
{
s2[c2]=d2[j];
c2++;
}
int c_2=0;//从d2的头开始匹配
for(i=c1-1;i>=0;i--)
{
if(s1[i].c=='?')
{
ans.push_back(make_pair(s1[i].num,s2[c_2].num));
c_2++;
}
else
{
break;
}
}
int c_1=0;//从d1的头开始匹配
for(j=c2-1;j>=c_2;j--)
{
if(s2[j].c=='?')
{
ans.push_back(make_pair(s1[c_1].num,s2[j].num));
c_1++;
}
else
{
break;
}
}
printf("%d\n",ans.size());
if(ans.size())
for(auto it:ans)
{
printf("%d %d\n",it.first,it.second);
}
return 0;
}
E.Superhero Battle
1.题目
E
2.解答
首先我们先判断一个回合,一个回合的特殊情况是
1.回合时间尚未结束,直接就打死了
2.一个回合所造成的伤害,为0或者小于0,那么此时就是没有办法打死的情况
接着,我们要求出一个回合能够造成的伤害D,以及一个回合内所能造成伤害的最大值dmax,我们用总的血量H/D,可得到一个大致的回合数R,但是由于可能dmax要大于D,所以可能在R回合之前,H就已经小于等于0了,我们应当调整R为R-dmax/D,这样可以解决上面问题,然后在这个回合的基础上,累计每分钟的伤害值,直到H小于或者等于0即可
#include <stdio.h>
#include <algorithm>
#include <math.h>
using namespace std;
const int maxn=4*1e5+7;
int damage[maxn];
long long int sum[maxn]={0};
long long int low=maxn;
int main()
{
long long int H;
long long int n;
scanf("%lld%lld",&H,&n);
long long int sum=0;
for(long long int i=0;i<n;i++)
{
scanf("%d",&damage[i]);
sum+=damage[i];
low=min(low,sum);
}
long long int oH=H;
for(long long int i=0;i<n;i++)
{
H+=damage[i];
if(H<=0)
{
printf("%d\n",i+1);
return 0;
}
}//判断第一个回合
long long int round=1;
long long int minute=0;
if(H>=oH)
{
printf("-1");
}
else
{
H=oH;
round=H/abs(sum);
long long int base=abs(low/sum);
H+=(round-base)*sum;
round-=base;
long long int result=round*n;
for(int i=0;;i++)
{
H+=damage[i%n];
result++;
if(H<=0)
{
printf("%lld",result);
break;
}
}
}
return 0;
}
F.Same Sum Blocks
1.题目
F
2.解答
由于F1和F2的题目几乎是一样的,所以我们放在一起来讲了。
首先我们记录下,从数组开头开始,所有和的可能的情况。并对其按照值的大小进行排列,如果值相同则会按照,右边节点从小到大排列,这里应用了贪心的思想,如果保证右边节点最小,那么右边的序列会更长一点,相同的可能也会更大一点。然后剩下的问题就相当于是,数组,求最大连续相同值的长度。
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
const int maxn=1507;
vector<pair<int,int> >ans;
vector<pair<int,int> >ans1;
struct node
{
int num;
int left;
int right;
};
node test[maxn];
node sum[1000*maxn];
bool cmp(node a,node b)
{
if(a.num==b.num)
{
return a.right<b.right;
}
return a.num<b.num;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&test[i].num);
test[i].left=i;
test[i].right=i;
}
int count=1;
for(int i=0;i<n;i++)
for(int j=1;j+i<=n;j++)
{
for(int k=i;k>=0;k--)
{
sum[count].num+=test[j+k].num;//求所有的段的和
}
sum[count].left=j;
sum[count].right=j+i;
count++;
}
ans1=vector<pair<int,int> >(n);
ans1.clear();
sort(sum+1,sum+count,cmp);
//for(int i=1;i<count;i++)
//{
//printf("%d %d %d\n",sum[i].num,sum[i].left,sum[i].right);
//}
int high=-1;
for(int i=1;i<count;i++)
{
int tar=sum[i].num;
int r=sum[i].right;
int count=1;
ans1.push_back({sum[i].left,sum[i].right});
for(int j=1;;j++)
{
if(sum[i+j].num==tar)
{
if(sum[i+j].left>r)//必须满足这个条件
{
r=sum[i+j].right;
count++;
ans1.push_back({sum[i+j].left,sum[i+j].right});
}
}
else
{
if(count>high)
{
high=count;
ans=ans1;
}
ans1.clear();
count=1;
i=i+j-1;
break;
}
}
}
printf("%d\n",ans.size());
for(auto it:ans)
{
printf("%d %d\n",it.first,it.second);
}
return 0;
}
G.Privatization of Roads in Treeland
1.题目
G
2.解答
这道题说实话,是真的没搞懂。所以去看了题解。整体来分析题目的意思就是说,在n个城市中最多有k个城市的两条或者两条以上的道路被同一个公司承包。我们将道路被一个公司承包想象为为这条道路染色。根据Dirichlet’s principle。当给图的边染色的时候,如果染色的种类小于最大度数,那么至少有一个点的两条边会染上相同的颜色。这一点还是比较容易证明的。同时我们还可以得到,度数高于颜色的种类的点,都至少会有两条边被染上相同的颜色。(解题的关键所在)。剩下的内容就是dfs的内容。具体见代码注释。(codeforces给的标程)
#include <iostream>
#include <vector>
#include <map>
using namespace std;
int n, k, r;
vector<vector<pair<int,int>>> g;
int D;
vector<int> col;
void dfs(int u, int p, int f) {
//u代表着当前遍历的点,
//p代表着遍历的上一个点,
//f表示进入该点的颜色是什么
int color = 0;
//每一个点的边的颜色从0开始,
//其实从多少开始都一样
for (auto e: g[u])
//遍历与该点向连的所有点
if (p != e.first) {
//去掉回到上一个点的情况
if (color == f) {
color = (color + 1) % D;
//第一次要避免边的颜色与上一条重复
//尽量为从该点出发的边上的颜色多一点
f = -1;
}
col[e.second] = color;
//为边染色
dfs(e.first, u, color);
color = (color + 1) % D;
//下一条边要染不同的颜色
//因为只有D种
//所以要去循环
}
}
int main() {
cin >> n >> k;
g.resize(n);
vector<int> d(n);
for (int i = 0; i + 1 < n; i++) {
int x, y;
cin >> x >> y;
x--, y--;
g[x].push_back({y, i});
//此处为记录与该点相连的所有边的信息;
g[y].push_back({x, i});
d[x]++, d[y]++;
//存储度数
}
map<int,int> cnt;
for (int dd: d)
cnt[dd]++;
//记录度数为dd的点有几个
int kk = n;
D = 0;
for (auto p: cnt)
if (kk > k)
D = p.first,
//p.first为度数
kk -= p.second;
//p.second为个数,
//表示剩下的度数顶点中,
//比现在颜色多的个数,
//要保证这一部分的数量不超过k
else
break;
col = vector<int>(n - 1);
dfs(0, -1, -1);
cout << D << endl;
for (int i = 0; i + 1 < n; i++)
cout << col[i] + 1 << " ";
return 0;
}
还没有评论,来说两句吧...