Codeforces Round #812 (Div. 2)

A. Traveling Salesman Problem

题目链接:Problem - A - Codeforces

样例输入: 

3
4
0 -2
1 0
-1 0
0 2
3
0 2
-3 0
0 -1
1
0 0

样例输出:

12
12
0

题意:给定一个坐标轴,在x轴和y轴上有一些物品,一开始有个人在原点,每一秒可以移动到相邻的格子,问最少需要多少秒可以捡完所有的物品并回到原点,捡物品是不耗费时间的。

分析:首先很明显的一点是由于物品都是在x轴和y轴上的,所以我们可以只在x轴和y轴上进行移动而没必要离开这两个坐标轴,因为我离开坐标轴后下一个想要拾取的物品一定是位于坐标轴的,所以我们可以把路径平移至坐标轴上,这样是一定存在最优方案的,按照贪心策略,我们每次向一个坐标轴的一个方向移动时一定会拾取完这个方向上的所有物品,也就是走到位于这个方向的最远物品处,这是很容易理解的,那么答案就是记录坐标轴每个方向的最远物品的位置,然后直接加上其到坐标原点的距离的2倍即可。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=1e5+10;
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		int xn,xm,yn,ym;
		xn=xm=yn=ym=0;
		int x,y;
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d",&x,&y);
			xn=min(xn,x);
			xm=max(xm,x);
			yn=min(yn,y);
			ym=max(ym,y);
		}
		printf("%d\n",2*(xm+ym-xn-yn));
	}
	return 0;
}

B. Optimal Reduction

题目链接:Problem - B - Codeforces

 样例输入:

3
4
2 3 5 4
3
1 2 3
4
3 1 3 2

样例输出:

YES
YES
NO

题意:给定一个长度为n的数组a,保证任何一个元素都是非负的,我们现在可以对数组a进行操作,每次操作可以选择一个区间[l,r],操作后a[l~r]自减,而f(a)就是数组a全部元素全变为0所需要的最少操作次数,现在询问,对于a数组的任意重拍后的数组b(重拍就是说对于a中的每个元素在b中一定有对应元素),是否有f(a)<=f(b)。

分析:其实通过模拟样例发现,对于一个数组a,如果要想满足他是所有重拍数组中的最小值,他的元素值就应该是先递增后递减的,这个画图是很容易想到的,所以我们只要判断一下a数组是否满足这个性质即可,极端情况就是一直递增或者一直递减,当然这也算是先递增后递减的一种。

细节见代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=1e5+10;
int a[N];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		int mxid=1;
		for(int i=2;i<=n;i++)
			if(a[i]>=a[mxid]) mxid=i;
		bool flag=true;
		for(int i=mxid;i>=2;i--)
			if(a[i]<a[i-1]) flag=false;
		for(int i=mxid;i<n;i++)
			if(a[i]<a[i+1]) flag=false;
		if(flag) puts("YES");
		else puts("NO"); 
	}
	return 0;
}

C. Build Permutation

题目链接:Problem - C - Codeforces

样例输入: 

3
3
4
7

样例输出:

1 0 2 
0 3 2 1 
1 0 2 6 5 4 3 

题意:先给定一个n代表数组元素的长度,数组的下标是从0~n-1,接下来输入n个数a[0~n-1],问能不能构造0~n-1的一个排列数组b[0~n-1]使得对于任意的0<=i<n均有a[i]+b[i]是一个完全平方数,如果没有输出-1,有的话就输出排列。

分析:

这道题是一道构造题,这道题是从后往前构造的,比如说我们要对0~n-1来构造,我们先想想怎么把n-1组成一个平方数,首先想到找到大于等于n-1的最小的平方数x,令y=x-(n-1),那么y与n-1组对,y+1与n-2组对,……,n-1与y组对,这些两两组合得到的值就是x,这样我们就可以把y~n-1的元素给配对成功,接下来就是对0~y-1的元素进行配对,这个时候可能会有这样一个疑问,有没有可能对于当前所要构造的最大元素n-1我们无法通过加一个0~n-1的数使得其变为x,也就是说大于等于n-1的最小平方数x>2*n-2,其实是不会的,很显然我们对n-1求平方根上取整后得到的数的平方就是小于等于2*n-2的,所以我们将其构造为这个平方数是一定可行的,因此是一定可以按这种构造方法进行构造的。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=1e5+10;
int a[N];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n,t;
		scanf("%d",&n);
		t=n-1;
		while(t>=0)
		{
			int tt=(int)sqrt(t);
			if(tt*tt!=t) tt++;
			tt*=tt;
			for(int i=tt-t;i<=t;i++)
				a[i]=tt-i;
			t=tt-t-1;
		}
		for(int i=0;i<n;i++)
			printf("%d ",a[i]);
		puts("");
	}
	return 0;
}

D. Tournament Countdown

题目链接:Problem - D - Codeforces

 样例输入:

1
3

2

0

2

样例输出:



? 1 4

? 1 6

? 5 7

! 7

题意:先来介绍一下锦标赛规则:有2^n个参赛选手进行比赛,总共分n轮比赛,每次比赛都会筛选掉一半的人,比如一开始1和2比较,若1赢了,则1晋级,2退出,每一轮比赛都是一半参赛选手晋级,另一半参赛选手退出,退出的参赛选手将不参与后续比赛,现在我们可以进行询问,对于每次询问我们可以给定两个数a和b,如果参赛选手a赢的场次多于b,那么返回1,如果参赛选手b赢的场次多于a,那么返回2,如果两个参赛选手赢的场次一样多就返回0.现在我们进行询问,询问次数不得超过,让我们在规定的询问次数内找到谁是最后的获胜者,每次只需要根据上一次的回复输出下一次我们要询问的对象a和b即可,最后输出获胜者的编号。

分析:首先我们能够发现第一轮比赛进行了2^(n-1)次,第二轮进行了2^(n-2)次,以此类推,最后进行2^(n-n)次,那么所有的比赛一共进行(2^n)-1次,而我们的询问次数大概是总比赛次数的2/3,也就是说我们能不能通过2次询问结果来得到原来三次询问结果所得到的结果,当然这么说有点抽象,举个简单的例子(就拿图中的例子吧,懒了p了就这样看吧)

 

 

按照正常思路我们需要先询问1和2的比赛结果(因为询问返回的是获胜次数之间的比较,但是我们知道1和2之间是有一场实际比赛的,谁获胜次数多肯定是谁晋级)得知是1晋级,然后询问3和4的比赛结果得知是4晋级,最后再询问一次1和4的比赛结果得知是4晋级,所以经过三次询问我们就知道了4是最后获胜的人,那么现在我们能不能通过两次询问就知道是谁最后获胜了呢?其实也可以,我们可以先问1和3的比赛结果,告知我们是1,说明3肯定不是最后获胜的,然后我们再询问1和4的比赛结果,告知我们是4,这个时候我们就可以知道是4最后获胜了,因为1比3多代表1晋级3未晋级,1晋级就代表2没有晋级,所以剩下的我们只需要比较1和4就行了,这是一种情况,那如果我们第一次问2和3呢?那么询问结果是0,也就是2和3获胜次数相同,说明他俩都没晋级,因为2和3获胜次数相同只可能是都晋级或者都没晋级,那么如果都晋级则两者之间一定有一次比赛,则不可能获胜次数相同,所以两者均未晋级,那么我们接下来只需要询问1和4即可,谁获胜次数多谁就是最后获胜的,分析到这我们发现原来需要3次询问得到的答案我们按照这种方法只需要两次就能得到,我们按照这个方法往上进行递推就能够在规定的询问次数内得到最终获胜者的编号。实验我是用的vector分层,标记每一层的参赛人员编号,每一次跳两层,跳至第一层时直接得到结果或者跳至第二层时通过1次询问得到结果。

需要注意的是交互题每次输出后都需要清缓存!!!

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=20;
vector<int>p[N];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		for(int i=0;i<N;i++) p[i].clear();
		int n;
		scanf("%d",&n);
		for(int i=1;i<=1<<n;i++) p[n+1].push_back(i);
		n++;
		int res;
		while(n>2)
		{
			for(int i=0;i<p[n].size();i+=4)
			{
				printf("? %d %d\n",p[n][i],p[n][i+2]);
				fflush(stdout);
				scanf("%d",&res);
				if(res==1)//p[n][i]赢的比p[n][i+2]多 
				{
					printf("? %d %d\n",p[n][i],p[n][i+3]);
					fflush(stdout);
					scanf("%d",&res);
					if(res==1)//p[n][i]赢的比p[n][i+3]多 
						p[n-2].push_back(p[n][i]);
					else
						p[n-2].push_back(p[n][i+3]);
				}
				else if(res==2)//p[n][i]赢的没有p[n][i+2]多 
				{
					printf("? %d %d\n",p[n][i+1],p[n][i+2]);
					fflush(stdout);
					scanf("%d",&res);
					if(res==1)//p[n][i+1]赢的比p[n][i+2]多 
						p[n-2].push_back(p[n][i+1]);
					else
						p[n-2].push_back(p[n][i+2]);
				}
				else//p[n][i]赢的和p[n][i+2]一样多,那么两者都没有获胜 
				{
					printf("? %d %d\n",p[n][i+1],p[n][i+3]);
					fflush(stdout);
					scanf("%d",&res);
					if(res==1)  p[n-2].push_back(p[n][i+1]);
					else p[n-2].push_back(p[n][i+3]);
				} 
			}
			n-=2;
		}
		if(n==2)
		{
			printf("? %d %d\n",p[n][0],p[n][1]);
			fflush(stdout);
			scanf("%d",&res);
			if(res==1)  p[1].push_back(p[n][0]);
			else p[1].push_back(p[n][1]);
		}
		printf("! %d\n",p[1][0]);
		fflush(stdout);
	}
	return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值