这道题启示我们。。有时候交互题也是可以做的。。。有时候真的不是你脸太黑的问题。。要对自己的脸有自信
题意:给出一个singly linked list,就是单向链表,size最多5W,是那种每个节点只知道自己的值以及下一个节点指针的链表。这个list已经升序排序好了,输入数字个数n,链表首地址st,以及询问x,要求你找到lower_bound(x)的值,就是第一个不小于x的值。链表对你不可见,但是你可以询问最多2000次。每次询问一个位置index,然后系统会告知你index位置的数据是value,并且下一个节点在nxt位置。
题解: 显然2000次询问踩5W个数字,别指望有什么算法可以保证得到正确解。别无他法,只好随机化。稍微动动脑子就知道,大量随机数,比如几百次,随机数的分布基本上是比较均匀的。而且题目中链表也是“随机”占位置的,可以认为基本上均匀出随机数。
当然这并没有什么卵用。具体思路是:先随机500次(也可以更多,注意不要重复。),然后我们取这500次随机询问中,value比x小的中,最大的一个。就是尽可能找到一个比较大的 小于x的数字。然后从这个位置开始顺序一个一个的询问,直到发现一个不小于x的输出,或者找到了nxt=-1就说明不存在比x大的数字。
概率证明:假设我们先随机500次然后走1500步找不到答案。假设正确答案在Y位置。那就说明我们前面500次随机数没有一个落在 到Y-1500 到 Y之间。那么随机一次不落在这个区间的概率是1-1500/50000=0.97,那么随机500次一次都不落在这个区间的概率是0.97^500=2e-7(千万分之二),也就是说如果有一千万组测试点,答案错误次数的期望值是2次。因此。。WA了只能说明脸太黑。。
Warning:rand()出随机数的范围是32767。本题千万不能用rand()出随机数字,因为32767-50000这么大的区间随机不到,这个跨度有一万多呢,答案要是在这里边直接GG。解决办法:rand()*rand()即可。
Code:
#include<bits/stdc++.h>
using namespace std;
const int MAX = 5e4+100;
const int MAGIC = 1000;
bool used[MAX];
int bestvalue;
int bestindex;
#define random(a,b) rand()*rand()%(b-a+1)+a
const int INF = 0x3f3f3f3f;
int value,nxt,x,n,st;
int main(){
srand(time(NULL));
scanf("%d%d%d",&n,&st,&x);
cout<<"? "<<st<<endl;
scanf("%d%d",&bestvalue,&nxt);
bestindex = st;
if (bestvalue>=x){
cout<<"! "<<bestvalue<<endl;
return 0;
}
if (n<1900){
while (true){
cout<<"? "<<bestindex<<endl;
scanf("%d%d",&value,&bestindex);
if (value>=x){
cout<<"! "<<value<<endl;
return 0;
}
if (bestindex==-1){
cout<<"! -1\n"<<endl;
return 0;
}
}
}
for (int i=0;i<MAGIC;i++){
int tt=random(1,n);
while (used[tt])tt = random(1,n);
used[tt] = true;
cout<<"? "<<tt<<endl;
scanf("%d%d",&value,&nxt);
if (value==x){
cout<<"! "<<value<<endl;
return 0;
}
if (value<x){
if (value>bestvalue){
bestvalue = value;
bestindex = tt;
}
}
}
for (int i=0;i<=795;i++){
cout<<"? "<<bestindex<<endl;
scanf("%d%d",&value,&nxt);
if (value>=x){
cout<<"! "<<value<<endl;
return 0;
}
if (nxt==-1){
cout<<"! -1"<<endl;
return 0;
}
bestindex = nxt;
}
cout<<"你脸黑"<<endl;
return 0;
}