E. Anya‘s Simultaneous Exhibition codeforces 1779E

11 篇文章 0 订阅
文章描述了一种策略来确定在给定棋手间胜负关系的比赛中的候选冠军。通过让所有棋手互相比赛并计算胜场数,可以找出至少能赢得半数比赛的棋手作为候选冠军。如果某人能打败已知的候选冠军,那么他也可能是候选冠军。通过询问和排序,可以确定所有可能的候选冠军,总询问次数不超过2n次。
摘要由CSDN通过智能技术生成

Problem - E - Codeforces

题目大意:有n个棋手,每两个棋手之间的胜负关系是确定的,每场比赛输的人要被淘汰,如果一个人在某种赛程安排中能够留到最后,就称他为候选冠军,现在为了能够知道谁是候选冠军,需要进行至多2n场比赛,每场比赛选出一个选手,与其他选出的任意个选手分别比赛,但不会有人淘汰,并给出他能赢这些人里面的几个,问都有谁是候选冠军

3<=n<=250

思路:首先,我们让所有人先和除他以外的所有人都比赛,那么那些胜场最多的人肯定能成为候选冠军,因为这个最大值至少是n/2,如果最大值是n-1,那么无论怎么排都能剩到最后,其他情况下,他们可以让他们打不过的人先被其他人淘汰,因为每个人肯定都是会有人淘汰他的,然后再和他打的过的人打。

其次,如果一个人能打过某个已经确定的候选冠军,那么它也是候选冠军,因为它可以先让候选冠军和其他人打,直到只剩那个冠军,再和冠军打,这样他也就成为了冠军,也就是说胜利关系其实是存在传递性的。

然后,如果一个胜场为x的人被确定为候选冠军,那么所有胜场大于等于x的人都能成为冠军,虽然这个结论看起来就很符合常理,但比较难证明,我们看具体的例子,例如n=5,编号为1,2,3,4,5的5个人胜场分别为3,2,2,2,1,我们假设2号赢得是1,3,这样的话3号赢得就是4,5,没有赢已知的候选冠军,然后根据上述条件我们可以确定下图为唯一的可能情况:

我们已经尽力避免了与2号胜场相同的3号成为候选冠军,但是因为5能淘汰2,3能淘汰5,所以3也是候选冠军,4能淘汰2,4也是候选冠军,所以上图中5人都是候选冠军,在其他的样例中哦们也能证明此结论是成立的。(如果有大佬知道怎么严谨证明,烦请留言指教)

那么有了以上结论的话,我们先n次询问得出每个人和其他所有人比的胜场,然后初始化最小冠军胜场x=最大的那个胜场数,然后将所有人按胜场数从多到少排序,然后对每个不是冠军的进行一次询问,让他与所有已确定的冠军比赛,如果有胜场,说明它也是候选冠军,维护最小胜场,然后将前面胜场大于等于x的没有被归为候选冠军的都归为候选冠军,总询问次数不超过2n

//#include<__msvc_all_public_headers.hpp>
#include<bits/stdc++.h>
using namespace std;
pair<int, int>a[255];//储存胜场数,编号
int main()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cout << "? " << i << " ";
		for (int j = 1; j <= n; j++)
		{
			if (j == i)
				cout << 0;
			else
				cout << 1;
		}
		cout << endl;
		cout.flush();
		int ou;
		cin >> ou;
		a[i] = make_pair(ou, i);
	}
	sort(a + 1, a + n + 1);//按胜场数排序
	map<int, int>ans;//储存每个人是否是候选冠军
	int mi = a[n].first;//成为候选冠军的最小胜场数
	int it = n;//从胜场数最多到小遍历
	while (a[it].first == mi)
	{//先将胜场数满足要求的归为候选冠军
		ans[a[it].second] = 1;
		it--;
	}
	while (it>=1)
	{
		int u = a[it].second;
		cout << "? " << u << " ";
		for (int i = 1; i <= n; i++)
		{
			if (ans.find(i) != ans.end())
			{//后所有当前已确定的候选冠军比赛
				cout << 1;
			}
			else
				cout << 0;
		}
		cout << endl;
		cout.flush();
		int ou;
		cin >> ou;
		if (ou)
		{//能赢过当前确定的候选冠军
			for (int i = it; i <= n; i++)
			{//胜场数大于mi的都是候选冠军
				if (ans[a[i].second] == 1)
					break;//后面的都是已经确定的候选冠军
				ans[a[i].second] = 1;
			}
			mi = a[it].first;//更新最小胜场数
		}	
		it--;
	}
	cout << "! ";
	for (int i = 1; i <= n; i++)
	{
		if (ans.find(i) != ans.end())
		{
			cout << 1;
		}
		else
			cout << 0;
	}
	cout << endl;
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timidcatt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值