题目大意:有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;
}