博弈论

博弈DP

POJ 1082: Calendar Game
从最终状态向前dp即可。
注意如下两点:
1 需要保证前后的状态均合法,例如f[i,j,k+1]=>f[i,j,k]则需要保证(i,j,k+1)和(i,j,k)这两个日期都合法。
2 因为日期是增加,所以要从后往前推,即循环逆序。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
从最终状态向前dp即可。
注意如下两点:
1 需要保证前后的状态均合法,例如f[i,j,k+1]=>f[i,j,k]则需要保证(i,j,k+1)和(i,j,k)这两个日期都合法。
2 因为日期是增加,所以要从后往前推,即循环逆序。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxy=2000+5;
const int maxm=12+5;
const int maxd=31+5;
const int month[][13]={0,31,28,31,30,31,30,31,31,30,31,30,31,
                        0,31,29,31,30,31,30,31,31,30,31,30,31};
const int INF=0x3f3f3f3f;
int T,y,m,d,f[maxy][maxm][maxd]={0};
int leap(int year){
    if(year%400==0||(year%4==0&&year%100!=0)) return 1;
    return 0;
}
void pre(){
    f[2001][11][4]=0;
    for(int i=2001;i>=1900;i--){
        int flag=leap(i),nxt_flag=leap(i+1);
        for(int j=12;j>=1;j--){
            if(i==2001&&j==12) continue;
            for(int k=month[flag][j];k>=1;k--){
                if(i==2001&&j>=11&&k>4) continue;
                //加一个月 
                if(j+1<=12&&k<=month[flag][j+1]&&(i<2001||(i==2001&&j+1<11)||(i==2001&&j+1==11&&k<=4))) 
                    f[i][j][k]|=!f[i][j+1][k];
                if(j==12&&k<=month[nxt_flag][1]&&(i+1<=2001)) 
                    f[i][j][k]|=!f[i+1][1][k];
                //加一天 
                if(k+1<=month[flag][j]&&(i<2001||(i==2001&&j<11)||(i==2001&&j==11&&k+1<=4))) 
                    f[i][j][k]|=!f[i][j][k+1];
                if(k==month[flag][j]&&j+1<=12&&(i<2001||(i==2001&&j+1<=11))) 
                    f[i][j][k]|=!f[i][j+1][1];
                if(k==month[flag][j]&&j==12&&(i+1<=2001)) 
                    f[i][j][k]|=!f[i+1][1][1];
            }
        }
    }
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Calendar Game.in","r",stdin);
    pre();
    cin>>T;
    while(T--){
        cin>>y>>m>>d;
        if(f[y][m][d]) cout<<"YES"<

POJ 2068: Nim
题解链接 http://www.mamicode.com/info-detail-2131299.html
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
题解链接 http://www.mamicode.com/info-detail-2131299.html 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=10+5;
const int maxs=(1<<13)+5;
const int INF=0x3f3f3f3f;
int d[maxn<<1][maxs],n,s,a[maxn<<1];
void init(){
    memset(d,-1,sizeof(d)); 
}
int dfs(int x,int y){ //轮到第i个人,还剩j个石子的胜负情况 
    if(d[x][y]!=-1) return d[x][y];
    if(y==0) return d[x][y]=1;
    for(int i=1;i<=a[x]&&i<=y;i++){
        if(!dfs(x%(2*n)+1,y-i)) return d[x][y]=1; //后继状态有一个必败 该状态必胜 
    }
    return d[x][y]=0; //后继状态都必胜 该状态必败 
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Nim.in","r",stdin);
    while(cin>>n){
        if(!n) break;
        cin>>s;
        for(int i=1;i<=2*n;i++) cin>>a[i];
        init();
        cout<

POJ 3688: Cheat in the Game
题解链接 http://www.hankcs.com/program/algorithm/poj-3688-cheat-in-the-game.html
注意每轮结束,石头数都会复原,因此博弈dp时每个卡片每轮中只能使用一次,即对应01背包的情况。
因为总共有四种状态(必败 必胜 非必败 非必胜)所以直接按照传统的转移会出现问题。
代码如下

/*
题解链接 http://www.hankcs.com/program/algorithm/poj-3688-cheat-in-the-game.html 
注意每轮结束,石头数都会复原,因此博弈dp时每个卡片每轮中只能使用一次,即对应01背包的情况。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=10000+5;
const int maxm=100000+5;
const int INF=0x3f3f3f3f;
int n,m,d[maxm][2],ans,a[maxn];
void dp(){
    d[0][0]=1;
    for(int i=1;i<=n;i++) for(int j=m;j>=a[i];j--) { //01背包 保证每个数字只能使用一次 
        if(d[j-a[i]][0]==1) d[j][1]=1;
        if(d[j-a[i]][1]==1) d[j][0]=1;
    }
}
void init(){
    ans=0;
    memset(d,0,sizeof(d));
}
int main() {
//  ios::sync_with_stdio(false);
    //freopen("Cheat in the Game.in","r",stdin);
    while(cin>>n>>m){
        if(!n&&!m) break;
        init();
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
//      sort(a+1,a+n+1);
        dp();
        for(int i=1;i<=m;i++) if(d[i][1]&&(!d[i][0])){
//          D(i);
            ans++;
        }
        cout<

POJ 1740: A New Stone Game
题解链接 https://www.cnblogs.com/s1124yy/p/5702694.html
代码如下

/*
题解链接 https://www.cnblogs.com/s1124yy/p/5702694.html 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=10+5;
const int maxm=100+5;
const int INF=0x3f3f3f3f;
int n,a[maxm];
int main() {
    ios::sync_with_stdio(false);
    //freopen("A New Stone Game.in","r",stdin);
    while(cin>>n){
        if(!n) break;
        int flag=0;
        for(int i=1;i<=n;i++) cin>>a[i];
        sort(a+1,a+n+1);
        if(n&1) flag=1;
        else{
            for(int i=1;i<=n;i+=2) if(a[i+1]!=a[i]) flag=1;
        }
        cout<

Nim和Grundy数

POJ 2975: Nim
设所有元素异或和为XOR,a[i]是原先的值,XOR^a[i]是为了让抑或值为0,必须将a[i]变成的值。
如果a[i]>=(XOR^a[i]),则可以通过改变a[i]来使得局面达到一个必败状态。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
设所有元素异或和为XOR,a[i]是原先的值,XOR^a[i]是为了让抑或值为0,必须将a[i]变成的值。
如果a[i]>=(XOR^a[i]),则可以通过改变a[i]来使得局面达到一个必败状态。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000+5;
const int INF=0x3f3f3f3f;
int n,a[maxn],XOR,ans;
void init(){
    XOR=ans=0;
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Nim.in","r",stdin);
    while(cin>>n){
        if(!n) break;
        init();
        for(int i=1;i<=n;i++) cin>>a[i],XOR^=a[i];
        if(XOR!=0){
            for(int i=1;i<=n;i++) if(a[i]>=(XOR^a[i])) ans++; 
            //a[i]是原先的值  XOR^a[i]是为了让抑或值为0 必须将a[i]变成的值 
        }
        cout<

POJ 3537: Crosses and Crosses
题解链接 https://www.cnblogs.com/neighthorn/p/6441252.html
注意vis要声明为局部变量,因为它只在这一层中有效。
代码如下

/*
题解链接 https://www.cnblogs.com/neighthorn/p/6441252.html 
注意vis要声明为局部变量,因为它只在这一层中有效。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=2000+5;
const int INF=0x3f3f3f3f;
int sg[maxn],n;
void init(){
    memset(sg,-1,sizeof(sg));
    sg[0]=0;
}
int dfs(int x){
    if(sg[x]!=-1) return sg[x];
    bool vis[maxn]; //注意vis要声明为局部变量 因为它只在这一层中有效
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=x;i++) vis[dfs(max(0,i-3))^dfs(max(x-i-2,0))]=1;
    for(int i=0;;i++) if(!vis[i]) return sg[x]=i;
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Crosses and Crosses.in","r",stdin);
    cin>>n;
    init();
    dfs(n)==0?cout<<"2":cout<<"1";
//  for(int i=1;i<=n;i++) D(sg[i]);
    return 0;
}

POJ 2315: Football Game
题解链接 http://www.hankcs.com/program/algorithm/poj-2315-football-game.html
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
题解链接 http://www.hankcs.com/program/algorithm/poj-2315-football-game.html 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=30+5;
const int logL=27+5; //log2(100000000)=27
const int INF=0x3f3f3f3f;
const double pi=acos(-1.0);
int n,m,l,r,XOR[logL],s[maxn]; 
void init(){
    memset(XOR,0,sizeof(XOR));
}
int dis(int s){
    return ((int)(s/(pi*r*2))+1);
}
bool solve(){
    int k=dis(l); //最大的石子堆数
    for(int i=1;i<=n;i++){
        int num=dis(s[i])%k; //额外的石子不管 降低时间复杂度
        int cnt=0;
        while(num){
            XOR[++cnt]+=num&1;
            num>>=1;
        } 
    } 
    for(int i=1;i<=logL-5;i++) if(XOR[i]%(m+1)) return true;
    return false;
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Football Game.in","r",stdin);
    while(cin>>n>>m>>l>>r){
        init();
        for(int i=1;i<=n;i++) cin>>s[i];
        solve()?cout<<"Alice"<

你可能感兴趣的:(博弈论)