题目链接:哆啦A梦传送门
题意:让你猜猜题目背后的a,b的值,可以问一些问题给题目,它会回答你问题,最多不超过62次提问,a,b不超过 2^30,
问问题的方式 ?c d ,然后题目会计算a^c-b^d,大于0,返回1,等于0,返回0,小于0,返回-1,当你可以猜出结果时,输出!a b。
题解:看到a b二进制为不超过30位,然后题目又给你62次问询,显然是有特定的问询,可以把a b二进制位每位按顺序找出来,关键是我们怎么问询以及怎么判断此位是0还是1。赛后看了官方标程,自己模拟了几组,得出了问询方式以及怎么判断此位是0还是1。
首先,我们从最高位(30)开始一直找到最低位,a b二进制位也就四种情况:
(1,1) (0,0) (1,0) (0,1)
问询方式,我们将原a b二进制位,分别单个取反,得出结果来,判断结果,具体见代码注释。
看完之后,举几个例子,例如:
1010 0101
从最高位开始,因为都是0,故f=1(左单取反1...1010 ,0...0101),s=-1(右单取反 0...1010,1...0101),故不满足判断条件,下一位
一直到第三位 ,f=-1(左单取反0...0010 ,0...0101),s=-1(右单取反 0...1010,1...1101),两值相等,故为(1,0)或(0,1)
因为big=1,big表示下一次位为(1,0)或(0,1)时,谁比较大,谁大就加到谁头上,那么此时big=-1,表示下一组位为(1,0)或(0,1)时,b比较大,要加到b头上。
再给两个:
1111 0111 和 1110 0111,自己模拟下,就会更理解得透彻。
这题还有个交互的考点,每次输出时,如果是printf输出的话,要在这之后加一句fflush(stdout)或cout<<flush();如果是cout的话,要加cout<<endl(endl有自动刷新缓冲池的功能);加这些语句的意思是,频繁循环输出,和 频繁循环交替输入输出 情况下,会不及时输出,而是等到缓冲区有一定数量内容时再输出。这种情况下,才用endl 或 flush 迫使 程序及时输出。
#include <iostream>
using namespace std;
int ask(int c,int d)
{
cout << "? " << c << ' ' << d << endl;
int ans;
cin >> ans;
return ans;
}
int main()
{
cout.flush();
int a=0,b=0,big=ask(0,0);///big的作用是,判断是否是(1,0),(0,1)时,原a b是否谁大
for (int i=29;i>=0;i--)
{
int f=ask(a^(1<<i),b),s=ask(a,b^(1<<i));///单个取反
if (f==s) ///相等说明 此位原a b为1 0或 0 1
{
if (big==1) ///谁大给谁当前位赋值为1
a^=(1<<i);
else if(big==-1) ///big等于0不出出现在这里判断,因为big=0的话,即这后面的每位都相等,故不会出现在此if语句
b^=(1<<i);
big=f;
}
else if (f==-1) ///此位原a b都为1,
{
a^=(1<<i);
b^=(1<<i);
}
///假如此位原a b为0,故f=1,s=-1,跳过,下一个位判断
}
cout << "! " << a << ' ' << b << endl;
}