随笔 - 58,  文章 - 0,  评论 - 4,  阅读 - 3296

一、题目描述:

  这是一道交互题,你需要猜出一个 1~n 的全排列 p1,p2,p3...pn

  有 t 组数据,每组数据有一个整数 n 表示数组的大小。

  假设一开始有一个只有 n 个点,没有边的图。你有 2×n次询问机会,两种询问方式:

  第一种:+ x。表示将 1 n 中所有 1xinini 连一条边。若连接成功,则回答 1,否则回答 1

  第二种:? u v。表示询问从 uv 的最小边数。若没有路径,则回答 1

  你有两次猜测序列的机会,只要有一个对了就算过关。请保证你输入的 x1~2×n 之间,uv1~n 之间。


 二、解题思路:

  乍一看这一题还真没什么思路。但是我们的目的是构造一条链,这样能简化问题,方便猜测。

  经过多次尝试后不难发现只需要 +   n+   n+1 就可以构成一条链。

 

  然后我们还剩下 2×n2 次询问机会,我们需要找出这一条链的一个端点,再根据端点的询问就可以解决这个问题。

  怎么求端点?感觉就跟求树的直径一样,随便找一个点,距离最远的一定就是端点。这里花掉 n1 次询问机会。

  最后再挨个询问其他各个点与端点的距离,最后输出即可。注意最后输出了答案还要读入一个 1 ,表示你的答案正确。

 

  但我的代码用的是别人的思路,一开始没想出来:

  任意挑选两个点对另外 n1 个点求距离,因为是链,任两点距离唯一,所以可以直接算出。

  设这两个点的位置为 x1,x2 (已知),要求的点位置为 x (未知)。距离分别为 dis1,dis2。给一个简单证明:

 

  |xx1|=dis1|xx2|=disx1!=x2

  x=x1±dis1x=x2±dis2

 

  Situation1:

  x1dis1=x2dis2x1+dis1=x2+dis2

  dis1dis2=x1x2dis1dis2=x2x1

  x1x2=x2x1<=>x2=x1

   x1!=x2

 

  Situation2:

  x1dis1=x2+dis2x1+dis1=x2dis2

  dis1+dis2=x1x2dis1+dis2=x2x1

  x1x2=x2x1<=>x2=x1

  x1!=x2

 

  

 

  而这个题显然是有解的,所以有唯一解。

  在计算的时候注意是否两个点求出的端点是同一个端点,要分类讨论。


 三、完整代码:

复制代码
 1 #include<iostream>
 2 #define N 1010
 3 using namespace std;
 4 int T,n,rans,a[N],vis[N];
 5 int ma[3],dis[3][N],ans[N];
 6 void add(int x)
 7 {
 8     cout<<"+ "<<x<<endl;
 9     cin>>rans;
10 }
11 int query(int u,int v)
12 {
13     cout<<"? "<<u<<" "<<v<<endl;
14     cin>>rans;    return rans;
15 }
16 void build_array()
17 {
18     for(int i=1;i<=n;i++)
19         vis[i]=0;
20     a[1]=(n+1)/2;vis[a[1]]=1;
21     for(int i=1;i<n;i++)
22     {
23         int t=n-a[i];
24         if(vis[t])    t++;
25         vis[t]=1,a[i+1]=t;
26     }
27 }
28 void print(int d1,int d2)
29 {
30     ans[1]=a[d1];ans[2]=a[d2];
31     for(int i=3;i<=n;i++)
32     {
33         int t11=d1+dis[1][i],t12=d1-dis[1][i];
34         int t21=d2+dis[2][i],t22=d2-dis[2][i];
35         if(t11==t21||t11==t22)    ans[i]=a[t11];
36         if(t12==t21||t12==t22)    ans[i]=a[t12];
37     }
38     for(int i=1;i<=n;i++)
39         cout<<ans[i]<<" ";
40 }
41 void solve()
42 {
43     cin>>n;
44     build_array();
45     ma[1]=ma[2]=0;
46     add(n);add(n+1);
47 
48     for(int i=1;i<=2;i++)
49         for(int j=1;j<=n;j++)
50             dis[i][j]=0;
51             
52     for(int i=1;i<=2;i++)
53         for(int j=1;j<=n;j++)
54             if(i!=j)
55             {
56                 dis[i][j]=query(i,j);
57                 if(dis[i][j]>dis[i][ma[i]])
58                     ma[i]=j;
59             }
60             
61     cout<<"! ";
62     if(ma[1]==ma[2])
63     {
64         print(1+dis[1][ma[1]],1+dis[2][ma[2]]);
65         print(n-dis[1][ma[1]],n-dis[2][ma[2]]);
66     }
67     if(ma[1]!=ma[2])
68     {
69         print(1+dis[1][ma[1]],n-dis[2][ma[2]]);
70         print(n-dis[1][ma[1]],1+dis[2][ma[2]]);
71     }
72     cout<<endl;cin>>rans;
73 }
74 int main()
75 {
76     cin>>T;
77     for(int i=1;i<=T;i++)
78         solve();
79     return 0;
80 }
复制代码

 四、写题心得:

  Codeforces 的题真的蛮有意思的,其实没考什么算法,但是很有趣!加油!拜拜!

posted on   trh0630  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示