题解:P6989 [NEERC 2014] Epic Win!
a_small_OIer · · 题解
P6989 [NEERC 2014] Epic Win!
你说的对,但是确定性算法太难想了。
题意
给定一个
思路
显然如果我们知道对方的初始状态,那我们一定可以达到
那么考虑如何知道对方的初始状态。一个显然的想法是通过一些出拳,根据对方后续的出拳来推测对方的初始状态。但是这个属于信息论范畴了,比较麻烦。
考虑随机化,我们钦定某个状态是对方的初始状态,根据对方的自动机进行预测对方的下一次出拳,如果预测正确,接着预测下下次,直到预测错误。如果预测错误,那么就重新钦定一个状态为当前状况,重复上述过程即可。
证明
显然每次随机猜测猜对的可能性是
设猜测错误的次数为
在
也就是说你有这么大的概率胜率没有
实现
#include<bits/stdc++.h>
#define N 110
#define L 500
using namespace std;
typedef long long LL;
//十年OI一场空,不开long long见祖宗
struct FSM{
int r , p , s;
}a[N];
int n , nxt[N][N] , out[N];
map<char , int> mp;
int id(int i , int j , int n){
return j * n + i;
}
int main(){
//freopen(".in" , "r" , stdin);
//freopen(".out" , "w" , stdout);
mt19937 rng(time(0));
mp['R'] = 0 , mp['P'] = 1 , mp['S'] = 2;
scanf("%d" , &n);
for(int i = 1 ; i <= n ; i++){
char c;
scanf(" %c" , &c);
out[i] = mp[c];
for(int j = 1 ; j <= 3 ; ++j)
scanf("%d" , &nxt[i][j]);
}
printf("%d\n" , L * n);
for(int i = 0 ; i < L ; ++i){
for(int j = 1 ; j <= n ; ++j){
int num = (out[j] + 1) % 3;
printf("%c" , "RPS"[num]);
for(int k = 0 ; k < 3 ; ++k){
int ns;
if(k == out[j])
ns = id(nxt[j][num + 1] , i , n);
else
ns = id(rng() % n + 1 , (i + 1) % L , n);
printf(" %d" , ns);
}
printf("\n");
}
}
return 0;
}