题意:有 n 家咖啡馆,每家咖啡店有一种咖啡,有些店的咖啡种类一样,你的朋友有 k 次记忆,你每次可以让你的朋友去一家咖啡馆喝咖啡,他会告诉你这个咖啡在之前的 k 次记忆中有没有喝过,你还有 h(<=30000) 次机会重置你朋友的记忆,在询问次数不超过 3*n^2 / 2k 的情况下,统计出共有多少种不同的咖啡;
分析:最暴力的方法是两两询问然后重置记忆,这样需要询问 (n-1)! 次,重置 (n-1)! 次,肯定会T,考虑有 n 种 咖啡的状态,所以我们必须每两家咖啡店都至少比较一次。因为你的朋友有 k 次记忆,则我们可以把 n 家咖啡馆分成 n/k 组,每组长度为 k ,只要每两组之间在询问中相邻过两次,并且两次前后顺序不同,那么就相当于这两组每两家咖啡店都比较了一次。。那么 问题来了,怎么让每两组分组在合理的询问次数内相邻两次且前后顺便不同呢,这个就得用到 zig-zag 顺序(x,x-1,x+1,x-2,x+2...),让分组按照这样的顺序询问 n 个咖啡店,并对重复出现的点标记(标记过后再不询问),每次询问完一遍后重置记忆,这样只要重置 n/k 次记忆,询问 n*n/k 次,就可以统计出有多少种不同的咖啡了。
代码:
#include<vector>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
bool ask(int pos)
{
cout<<"? "<<pos+1<<endl<<flush;
char c; cin>>c;
return c=='Y';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,k,p;
cin>>n>>k; p=n/k;
vector<bool>cnt(n,true);
for(int i=0;i<p;i++){
int shift=0;
for(int j=0;j<p;j++){
int now=(i+shift+p)%p;
int bg=k*now;
for(int z=bg;z<bg+k;z++){
if(cnt[z]){
if(ask(z)) cnt[z]=false;
}
}
if(shift>=0) shift++;
shift=-shift;
}
cout<<"R "<<endl<<flush;
}
int ans=count(cnt.begin(),cnt.end(),true);
cout<<"! "<<ans<<endl<<flush;
}
来源:CSDN
作者:伴君
链接:https://blog.csdn.net/weixin_43209425/article/details/104220156