BZOJ2798/POI 2012 Bidding

1 篇文章 0 订阅

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 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;
} 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值