原题链接: https://codeforces.com/contest/1407/problem/C
测试样例
inputCopy
3
1
2
1
0
output
? 1 2
? 3 2
? 1 3
? 2 1
! 1 3 2
题意: 这是一个交互问题,有一个1~n的排列数组,你并不知道它的排列,现在你想通过向系统询问来猜想排列数组。你最多有
次的询问机会。询问方式为
,意为询问
的结果。系统会返回给你值。
解题思路: 这道题是一道许久未见的交互题,出的非常好(吹爆cf)。我们首先要知道一个性质,就是我们询问的是,其中
和
是不相等的,那么如果我们还询问
。有了这两个的值,我们就可以知道其中一个结果了。因为根据取余性质可得:小数mod大数结果还是为小数,而大数mod小数结果一定比小数小。据此解题。 我们判断系统返回的两个结果比较大小不就可以确定原排列中的一个数了吗?由于我们最多有
的询问,我们每确定一个就需要2次询问,这完全是够的。(当然,我们去询问时一定要使用两个都未确定的下标。),当然我们最多只能确定
个数,因为最大的数无法被确定,所以我们只要询问
次。那么你可能觉得奇怪了,最大的数怎么确定呢?当然可以,我们已经知道了
个数。那么最后一个数自然是
,它的下标也自然已知。OK,据此题易解。(注意细节,我们假定一个变量储存未确定的下标,要实时更新,初始为1。然后遍历2~n去询问确定。)
AC代码
/*
*
*/
//POJ不支持
//i为循环变量,a为初始值,n为界限值,递增
//i为循环变量, a为初始值,n为界限值,递减。
using namespace std;
const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 1e5;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll> pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为自定义代码模板***************************************//
int n,p[maxn];
int main(){
//freopen("in.txt", "r", stdin);//提交的时候要注释掉
IOS;
while(cin>>n){
memset(p,-1,sizeof(p));
int temp=1;//以temp为固定对比参考
int p1,p2;//存储系统返回的值。
rep(i,2,n){
cout<<"? "<<temp<<" "<<i<<endl;
cout.flush();//吸收系统返回值。
cin>>p1;//传递给变量。
cout<<"? "<<i<<" "<<temp<<endl;
cout.flush();
cin>>p2;
//根据mod可知,小数mod大数结果还是为小数,而大数mod小数结果一定比小数小。据此解题。
if(p1>p2){
//说明p[temp] mod p[i]=p[temp];
p[temp]=p1;//则此时p1已确定,我们要选择未确定的继续进行处理。
temp=i;
}
else{
p[i]=p2;//说明temp未确定,我们此时无需更换。
}
}
//接下来输出,由于我们只能套出最小的值。最大的值始终是套不出的,所以我们最后剩余的temp就可以填充temp。
p[temp]=n;
cout<<"! ";
rep(i,1,n){
cout<<p[i]<<" ";
}
cout<<endl;
}
return 0;
}