一个地方有两种人:诚实的人和骗子,诚实的人只说真话,骗子可能说真话也可能说假话,已知的是这里诚实的人比骗子要多,而且当地人知道这里其他的人是骗子还是诚实的人。
你来到这里,要求找出所有的诚实的人,你可以问这里任何的人A关于另外个人B的问题“B是不是骗子?”。设计一个找出所有诚实的人的算法,但是时间复杂度是O(n)
# include <iostream>
# include <cstdio>
# include <cstdlib>
using namespace std;
typedef pair<int,int> PII;
const int N=1e6+5;
int head,e[N],ne[N],idx=1; //数组模拟带头链表
int n;
//链表操作
void init()
{
ne[head]=-1;
}
void insert(int x)
{
e[idx]=x,ne[idx]=ne[head],ne[head]=idx++;
}
void del(int fa)
{
ne[fa]=ne[ne[fa]];
}
void output()
{
int p=ne[head];
while(p!=-1)
{
printf("%d ",e[p]);
p=ne[p];
}
printf("\n");
}
//模拟询问
PII ask(int x,int y)
{
PII ans;
if(x)
{
if(y)ans.first=1,ans.second=1;
else ans.first=0,ans.second=rand()%10>5?1:0;
}
else
{
if(y)ans.first=rand()%10>5?1:0,ans.second=0;
else ans.first=rand()%10>5?1:0,ans.second=rand()%10>5?1:0;
}
return ans;
}
//检查操作
bool check(PII ans)
{
int x=ans.first,y=ans.second;
if(x&&y)return false;
return true;
}
int main()
{
init();
/*
输入第一行为人数n,第二行中1代表诚实者,0代表不诚实者
输入样例:
3
0 1 1
*/
cin>>n;
int t;
for(int i=0;i<n;++i)scanf("%d",&t),insert(t);
if(n%2)insert(1); //人数为奇数则增加一个诚实者,不影响原序列
//output();
while(ne[ne[head]]!=-1) //链表中只有一个元素时跳出
{
int fa=head,p=ne[fa]; //头结点 第一个有效结点
while(1)
{
PII ans=ask(e[p],e[ne[p]]);
//cout<<ans.first<<ans.second<<endl;
if(check(ans))del(p),del(fa);
else del(p),fa=ne[fa];
if(ne[fa]==-1||ne[ne[fa]]==-1)break; //指针向后移2个结点,若剩余不足两个结点则跳出
p=ne[fa];
//output();
}
}
output();
return 0;
}