BZOJ2798/POI 2012 Bidding

来源:互联网 发布:生命公式软件 编辑:程序博客网 时间:2024/06/05 08:01

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=2a3b.
那么这样的x就不多了,直接dp即可.

#include<cstdio>#include<cstring>#include<algorithm>#include<cliclib.h>#define my_op alojzy#define he_op bajtazarusing 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;} 
0 0