交互题专题
前言
交互题是一类特殊的算法题, 每次输入数据, 系统都会给出数据进行反馈, 需要通过系统反馈的数据, 得到某个答案. 交互题在蓝桥杯和OI比赛中都不会考察, CF偶尔会有交互题. 交互题的输入输出比较特别, 需要了解交互题独有的输入输出格式. 最近参加的团队程序设计天梯赛, 模拟赛中考察了交互题, 觉得交互题还是有必要记录一下的.
交互题介绍
通俗来讲, 交互题与平时题目的输入输出反过来, 是让你设计程序去向用户提出询问, 由用户给出答案, 并且在这基础上由程序推断出正确答案的一种形式.
C++的交互题, 每次输出后都得加 fflush(stdout)
交互题的特殊错误:
- 选手每一次输出后都需要刷新缓冲区, 否则会引起 Idleness limit exceeded 错误. 另外, 如果题目含多组数据并且数据可以在未读入所有数据前就知道答案, 也仍然要读入所有数据, 否则同样会因为读入混乱引起 ILE (可以一次提出多次询问, 一次接收所有询问的回答). 同时尽量不要使用快读.
- 如果程序查询次数过多, 则在 Codeforces 上会给出 Wrong Answer 的评测结果(不过评测系统会说明 Wrong Answer 的原因), 而 UVA 会给出 Protocol Limit Exceeded (PLE) 的评测结果.
例题
Codeforces Round 356 (Div. 1) A. Bear and Prime 100
题目大意: 系统生成了一个数, 你需要在20次询问内给出这个数是质数还是合数. 每次询问一个数, 它会回答这个数是不是它的因数.
思路: 询问[2, 50] 内的质数, 如果该质数x是它的因数, 则继续询问\(x^{2}\)是不是它的因数(判断是否这个数就是这个质数x), 若该数在[2, 100] 范围内有两个及以上的因数, 则为合数. 若只有一个因数(它本身), 则为质数.
注意: C++的交互题, 每次输出后都得加 fflush(stdout)
#include<iostream>
#include<algorithm>
using namespace std;
bool is_prime(int x)
{
for(int i=2;i<=x/i;i++)
if(x%i==0)return false;
return true;
}
bool divisible(int x)
{
cout<<x<<'\n';
fflush(stdout);
string str;
cin>>str;
if(str=="yes")return true;
else return false;
}
int main()
{
int maxx=100;
int cnt=0;
for(int i=2;i<=maxx/2;i++)
if(is_prime(i))
{
if(divisible(i))
{
cnt++;
if(i*i<=maxx)
if(divisible(i*i))cnt++;
}
}
if(cnt>=2)cout<<"composite"<<'\n';
else cout<<"prime"<<'\n';
fflush(stdout);
return 0;
}
Codeforces Round 859 (Div. 4) E. Interview
题目大意: 给定n堆石头, 并告诉你第i堆石头的重量是ai, 但实际上有一堆石头的重量比告知的大1, 需要在30次询问内, 找出这是第几堆石头. 每次询问, 先输入询问的石头堆数, 再输入石头堆的下标, 系统会回答这些石头实际的总重量.
思路: 二分、前缀和
#include<iostream>
#include<algorithm>
using namespace std;
const int N=200010;
int a[N],s[N];
bool ask(int l,int r)
{
cout<<"?"<<' ';
cout<<r-l+1<<' ';
for(int i=l;i<=r;i++)cout<<i<<' ';
fflush(stdout);
long long sum=s[r]-s[l-1];
long long ans;
cin>>ans;
return sum==ans;
}
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=s[i-1]+a[i];
}
int l=1,r=n;
while(l<r)
{
int mid=(l+r)/2;
if(ask(l,mid))l=mid+1;
else r=mid;
}
cout<<"!"<<' '<<l<<'\n';
fflush(stdout);
}
return 0;
}
Technocup 2017 - Elimination Round 1 (Unofficially Open for Everyone, Rated for Div. 2)
题目大意: 系统生成了一个长度为n (\(n\ge\) 3) 的数列, 你需要经过不超过n次询问, 猜出这个数列. 每次询问, 你可以输入两个下标 i 和 j (i \(\neq\) j , 且 i , j 都属于1到n) , 系统会回复ai + aj 的和.
思路: 先询问 a1 + a2 , a2 + a3 , a3 + a1 , 得到 a1 , a2 , a3 的值, 之后(n - 3)次, 每次询问a1 和 ai , 得到 ai 的值.
#include<iostream>
#include<algorithm>
using namespace std;
const int N=5010;
int a[N];
int n;
int main()
{
cin>>n;
int one,two,three,sum;
cout<<"? 1 2"<<'\n';
fflush(stdout);
cin>>three;
cout<<"? 1 3"<<'\n';
fflush(stdout);
cin>>two;
cout<<"? 2 3"<<'\n';
fflush(stdout);
cin>>one;
sum=(one+two+three)/2;
a[1]=sum-one;
a[2]=sum-two;
a[3]=sum-three;
for(int i=4;i<=n;i++)
{
cout<<"? 1 i"<<'\n';
fflush(stdout);
int x;
cin>>x;
a[i]=x-a[1];
}
cout<<"!"<<' ';
for(int i=1;i<=n;i++)cout<<a[i]<<' ';
fflush(stdout);
return 0;
}