Task
A和B两个人在玩一个游戏,这个游戏是他们轮流操作一对整数(x,y)。
初始时(x,y)=(1,0),可以进行三种操作:
1. 将(x,y)变成(1,x+y)。
2. 将(x,y)变成(2x,y)。
3. 将(x,y)变成(3x,y)。
给定正整数n (n<=30,000),如果x+y>=n时就不能进行后两种操作。
如果某个人操作后y>=n,他就输掉了。
假如A为先手,问他是否有必胜策略。
这题是道交互题,需要包含cliclib.h头文件,有下面三个函数可以使用:
1. int inicjuj(); 开始时调用,返回n的值。
2. void alojzy(int x); A进行一次操作,x表示操作编号。
3. int bajtazar(); 获得B的操作编号。
例如下面的程序每次选择操作1:
#include "cliclib.h"
int main() {
int n = inicjuj();
while (true) {
alojzy(1);
int x = bajtazar();
}
return 0;
}
Solution
对于”我”来说,为了赢交互库,需要每一步都采取最优策略,那么只要求出当前局面的后继局面中那些是必败态即可.
那么只要预处理出每个状态的性质就可以完成任务了.
问题是如何定义状态,x,y都可能很大,数组是存不下的,但是观察游戏的过程可以发现当前的x只可能是2的乘幂与3的乘幂的乘积,也就是
x=2a∗3b
.
那么这样的x就不多了,直接dp即可.
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cliclib.h>
#define my_op alojzy
#define he_op bajtazar
using namespace std;
const int M=30005;
const int S2=16,S3=11;
int f2[S2],f3[S3];
bool dp[S2][S3][M];
int inicjuj(){return 0;}
void my_op(int a){};
int he_op(){return 0;}
int main(){
int i,j,k,n=inicjuj();
int sum=0,a=0,b=0;
f2[0]=f3[0]=1;
for(i=1;i<S2;i++)f2[i]=f2[i-1]*2;
for(i=1;i<S3;i++)f3[i]=f3[i-1]*3;
for(k=n;k>=0;k--){
for(i=S2-1;i>=0;i--){
for(j=S3-1;j>=0;j--){
int v=k+f2[i]*f3[j];
if(v>=n)dp[i][j][k]=false;
else{
int f=0;
if(i+1>=S2||j+1>=S3)f|=1;
else if(!dp[0][0][v]||!dp[i+1][j][k]||!dp[i][j+1][k])f=1;
dp[i][j][k]=f;
}
}
}
}
while(true){
if(a+1>=S2||!dp[a+1][b][sum])my_op(2),a++;
else if(b+1>=S3||!dp[a][b+1][sum])my_op(3),b++;
else my_op(1),sum+=f2[a]*f3[b],a=b=0;
int y=he_op();
if(y==1){sum+=f2[a]*f3[b];a=b=0;}
else if(y==2)a++;
else b++;
}
return 0;
}