博弈論小結

這幾天充分感覺了被博弈論支配的恐懼……php

首先是參考資料:html

學長的一些課件……就不放了node

而後朱全民老師的課件ios

翻硬幣遊戲ide

樹上刪邊遊戲函數

還有這個dalao的模型總結挺好的,種類也很多……http://www.javashuo.com/article/p-zinqyvlq-bd.htmlui

而後……具體模型sg函數sj定理以及各類詭異的遊戲請看上面的參考資料……我只是瞎扯一些作題的感想spa

首先學了下極大極小搜索以及配套的alpha-beta剪枝.net

雖然……並非我當時作的那道題的正解……3d

可是這種思想很好,尤爲是剪枝的時候

咱們在搜的時候保留以前祖先節點的上界下界,若是不可能更新最優解了直接跳出。

大概貼的代碼……那個原題是在棧裏面取東西因此代碼打成這樣了

 1 int f[N][N<<1][2];
 2 inline int min(int a,int b){return a<b?a:b;}
 3 inline int max(int a,int b){return a>b?a:b;}
 4 inline int searching(int player,int layer,int limit,int down,int up)
 5 {
 6     if(layer>n)return 0;
 7     if(f[layer][limit][player]!=-1)return f[layer][limit][player];
 8     register int i,lim=min(layer+limit-1,n),v;
 9     for(i=layer;i<=lim;++i)
10     {
11         v=searching(player^1,i+1,(i-layer+1)<<1,down,up);
12         if(!player)down=max(down,v);
13         else up=min(up,v);
14         if(down>up)break;
15     }
16     return f[layer][limit][player]=(player?up:down);
17 }
極大極小

而後咱們來講說那道題的正解:博弈DP

博弈DP在轉移的時候利用了一個東西:因爲兩我的都是絕頂聰明的,因此面臨一樣狀態時決策必定會是相同的

而後對於本題的轉移方程,因爲對手必定會取最有利的局面,所以咱們轉移的時候不能取max而要取min……

看一下代碼:

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 #define N 2010
 5 inline int min(int a,int b){return a<b?a:b;}
 6 inline int max(int a,int b){return a>b?a:b;}
 7 int f[N][N],n,val[N];
 8 int main()
 9 {
10     // freopen("Ark.in","r",stdin);
11     register int i,j;scanf("%d",&n);
12     for(i=1;i<=n;++i)scanf("%d",&val[i]);
13     for(i=n;i;--i)val[i]+=val[i+1];
14     for(i=1;i<=n;++i)
15         for(j=1;j<=n;++j)
16         {
17             f[i][j]=f[i][j-1];
18             if(i-2*j+1>=0)f[i][j]=max(f[i][j],val[n-i+1]-f[i-2*j+1][2*j-1]);
19             if(i-2*j>=0)f[i][j]=max(f[i][j],val[n-i+1]-f[i-2*j][2*j]);
20         }
21     printf("%d\n",f[n][1]);
22 }
bzoj2017

接着下一題……

這題以及後面的某些題大概是對每一個下標的元素計算sg函數值,而後異或

這多是一種解題技巧……

對於本題,對於下標i,咱們考慮全部在i操做以後影響到的狀態的mex值,

即咱們找出全部操做以後受影響的元素,把他們異或起來做爲狀態,而後再取mex

由於sg函數計算就是對到達的狀態取mex嘛……

代碼:

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 #define N 25
 5 int n,sg[N];
 6 inline int read()
 7 {
 8     int x=0;register char c=getchar();
 9     while(c<'0'||c>'9')c=getchar();
10     while(c>='0'&&c<='9')x=10*x+(c^48),c=getchar();
11     return x;
12 }
13 bool vis[40];
14 inline int get(int id)
15 {
16     register int i,j;
17     memset(vis,0,sizeof(vis));
18     for(i=id+1;i<n;++i)
19         for(j=id+1;j<n;++j)vis[sg[i]^sg[j]]=1;
20     for(i=0;i<=n*2;++i)if(!vis[i])return i;
21     return n;
22 }
23 int main()
24 {
25     // freopen("Ark.in","r",stdin);
26     register int i,j,k,ans,cnt,t=read();
27     while(t--)
28     {
29         n=read(),ans=cnt=0;
30         for(i=n-1;~i;--i)sg[i]=get(i);
31         for(i=0;i<n;++i)if((read()&1))ans^=sg[i];
32         for(i=0;i<n;++i)
33             for(j=i+1;j<n;++j)
34                 for(k=j;k<n;++k)
35                     if(!(ans^sg[i]^sg[j]^sg[k]))
36                     {
37                         ++cnt;
38                         if(cnt==1)printf("%d %d %d\n",i,j,k);
39                     }
40         if(!cnt)puts("-1 -1 -1");
41         printf("%d\n",cnt);
42     }
43 
44 }
bzoj1188

而後再來一道也是sg和下標有關的題目……

 咱們認爲……這樣的翻硬幣相似物遊戲中每一個白點是獨立的

也就是說咱們把每一個下標的sg值都異或起來而後判斷便可

 至於怎麼算……這道題咱們使用除法分塊來作

因爲後面n/2長度的sg值相等,那麼他們前面一段的sg值也相等,這樣推到前面去,在同一個除法分塊塊裏面的下標sg值都相等

因此咱們就能夠用根本跑不滿的$O(n)$來打這道題了!

代碼:

 1 #include <cstdio>
 2 #include <algorithm>                                                                                                                                                                                        
 3 #include <cstring>
 4 #include <cmath>
 5 using namespace std;
 6 #define N 66000
 7 bool vis[1010];
 8 int len,hd[N],tot,cnt,l[N],r[N],sg[N],f[N];
 9 inline void init(int n)
10 {
11     register int i,j,cur,val,last;
12     for(i=1;i<=n;i=last+1)
13         hd[++tot]=last=n/(n/i);
14     r[0]=n,cnt=0;
15     for(i=tot;i;--i)
16     {
17         val=0,cur=cnt;
18         for(last=j=2;j<=n/hd[i];j=last+1)
19         {
20             while(cur&&j*hd[i]>r[cur])--cur;
21             last=r[cur]/hd[i];
22             vis[val^f[cur]]=1;
23             if((last-j+1)&1)val^=f[cur];
24             vis[val]=1;
25         }
26         for(j=1;vis[j];++j);
27         sg[i]=f[++cnt]=j;
28         if(cnt>1&&f[cnt]==f[cnt-1])l[--cnt]=hd[i-1]+1;
29         else l[cnt]=hd[i-1]+1,r[cnt]=hd[i];
30         memset(vis,0,sizeof(vis));
31     }
32 }
33 inline int gsg(int pos)
34     {return sg[upper_bound(hd+1,hd+tot+1,pos)-hd-1];}
35 int main()
36 {
37     // freopen("Ark.in","r",stdin);
38     register int i,j,t,n,a,ans;
39     scanf("%d%d",&len,&t);
40     init(len);
41     while(t--)
42     {
43         scanf("%d",&n),ans=0;
44         for(i=1;i<=n;++i)
45             scanf("%d",&a),ans^=gsg(len/(len/a));
46         puts(ans?"Yes":"No");
47     }
48 }
bzoj4035

而後……下面這道題和sg函數並無什麼關係……

題面

而後呢……咱們發現照題意這樣操做,一次操做的後繼狀態就太!多!了!

因而咱們考慮手玩小樣例可能這也是解題技巧吧2333

總之結論是」原石子能夠被分紅徹底相同的2堆「的時候先手必敗

至於具體的證實……

咱們採起什麼操做,咱們對手就利用和咱們取的那個石子堆對稱的那堆進行操做就好了,徹底模仿便可。

而後若是不徹底相同,咱們能夠用最大的那堆去把其餘的補齊,使得他們能成爲徹底相同的2組,而後把最大的扔到跟最小的相等

這時候咱們對手就必敗了……

而後代碼也很簡單啦……

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 char B[1<<15],*S=B,*T=B;
 6 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
 7 inline int read()
 8 {
 9     int x=0;register char c=getc;
10     while(c<'0'||c>'9')c=getc;
11     while(c>='0'&&c<='9')x=10*x+(c^48),c=getc;
12     return x;
13 }
14 int a[100010];
15 int main()
16 {
17     // freopen("Ark.in","r",stdin); 
18     register int n,i,j;
19     for(n=read(),i=1;i<=n;++i)a[i]=read();
20     for(sort(a+1,a+n+1),i=1;i<=n;++i)
21         if(a[i]!=a[i+1]){puts("first player");return 0;}
22     puts("second player");return 0;
23 }
bzoj1982

 而後作了個階梯博弈……彷佛頗有理有據的證實,你們想看能夠去上面的參考資料找。

不過這題好毒啊,居然要打ETT

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cstdlib>
  4 #include <iostream>
  5 #include <algorithm>
  6 using namespace std;
  7 char B[1<<15],*S=B,*T=B;
  8 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
  9 inline int read()
 10 {
 11     int x=0;register char c=getc;
 12     while(c<'0'||c>'9')c=getc;
 13     while(c>='0'&&c<='9')x=10*x+(c^48),c=getc;
 14     return x;
 15 }
 16 #define N 100010
 17 int n,lim,e,adj[N],fa[N],deep[N],val[N];
 18 struct edge{int zhong,next;}s[N];
 19 inline void add(int qi,int zhong)
 20     {s[++e].zhong=zhong;s[e].next=adj[qi];adj[qi]=e;}
 21 struct node
 22 {
 23     node *ch[2],*f;
 24     int val,val0,val1,deep;
 25     node(){val0=val1=0;}
 26     inline void update()
 27     {
 28         val0=ch[0]->val0^ch[1]->val0;
 29         val1=ch[0]->val1^ch[1]->val1;
 30         if(deep&1)val0^=val;else val1^=val;
 31     }
 32 }*null,*root,mem[N<<1],*l[N],*r[N],*sta[N<<1],*tp;
 33 inline void init()
 34 {
 35     null=new node();
 36     null->ch[0]=null->ch[1]=null->f=null;
 37     null->val=null->val0=null->val1=0;
 38 }
 39 int top,tot;
 40 inline bool isroot(node *o){return o->f==tp;}
 41 inline int son(node *o){return o->f->ch[1]==o;}
 42 inline node* newnode(int val,node *f,int dp)
 43 {
 44     node *o=mem+(tot++);
 45     o->ch[0]=o->ch[1]=null,o->f=f;
 46     o->deep=dp,o->val=val;
 47     if(dp&1)o->val0=val;else o->val1=val;
 48     return o;
 49 }
 50 inline void dfs(int rt)
 51 {
 52     deep[rt]=deep[fa[rt]]+1;
 53     l[rt]=newnode(val[rt],null,deep[rt]);sta[++top]=l[rt];
 54     for(int i=adj[rt];i;i=s[i].next)dfs(s[i].zhong);
 55     r[rt]=newnode(0,null,deep[rt]);sta[++top]=r[rt];
 56 }
 57 inline void rotate(node *o)
 58 {
 59     node *fa=o->f,*grand=fa->f;
 60     int k=son(o),kk=son(fa);
 61     fa->ch[k]=o->ch[k^1];
 62     if(o->ch[k^1]!=null)o->ch[k^1]->f=fa;
 63     o->ch[k^1]=fa,fa->f=o,o->f=grand;
 64     if(grand!=null)grand->ch[kk]=o;
 65     fa->update(),o->update();
 66 }
 67 inline void splay(node *o,node *towards)
 68 {
 69     for(tp=towards;!isroot(o);rotate(o))
 70         if(!isroot(o->f))rotate(son(o)==son(o->f)?o->f:o);
 71 }
 72 inline node* getpre(node *o)
 73 {
 74     splay(o,null),o=o->ch[0];
 75     while(o->ch[1]!=null)o=o->ch[1];
 76     return o;
 77 }
 78 inline node* getback(node *o)
 79 {
 80     splay(o,null),o=o->ch[1];
 81     while(o->ch[0]!=null)o=o->ch[0];
 82     return o;
 83 }
 84 inline node* get_range(node* a,node* b)
 85 {
 86     node *o=getpre(a),*oo=getback(b);
 87     splay(o,null),splay(oo,o);return oo->ch[0];
 88 }
 89 inline node* build(int l,int r)
 90 {
 91     if(l>r)return null;
 92     register int mi=l+r>>1;
 93     sta[mi]->ch[0]=build(l,mi-1),sta[mi]->ch[1]=build(mi+1,r);
 94     if(sta[mi]->ch[0]!=null)sta[mi]->ch[0]->f=sta[mi];
 95     if(sta[mi]->ch[1]!=null)sta[mi]->ch[1]->f=sta[mi];
 96     sta[mi]->update();return sta[mi];
 97 }
 98 inline int query(int id)
 99 {
100     int ret;
101     node *o=get_range(l[id],r[id]);
102     if(deep[id]&1)ret=o->val1!=0;
103     else ret=o->val0!=0;
104     puts(ret?"MeiZ":"GTY");
105     return ret;
106 }
107 inline void update(int id,int v)
108 {
109     splay(l[id],null);
110     l[id]->val=val[id]=v;
111     l[id]->update();
112 }
113 inline void insert(int f,int id)
114 {
115     node *o=getpre(r[f]);
116     splay(r[f],null),splay(o,r[f]);
117     l[id]=newnode(val[id],null,deep[id]);
118     r[id]=newnode(0,null,deep[id]);
119     l[id]->ch[1]=r[id],r[id]->f=l[id],l[id]->update();
120     o->ch[1]=l[id];l[id]->f=o;o->update();
121 }
122 int main()
123 {
124     // freopen("Ark.in","r",stdin);
125     register int i,a,b,c,m,cnt=0,opt;
126     n=read(),lim=read();
127     for(i=1;i<=n;++i)val[i]=read()%(lim+1);
128     for(i=1;i<n;++i)a=read(),b=read(),fa[b]=a,add(a,b);
129     init();
130     l[0]=newnode(0,null,0),sta[++top]=l[0];
131     for(i=1;i<=n;++i)if(!fa[i]){dfs(i);break;}
132     r[0]=newnode(0,null,0),sta[++top]=r[0];
133     root=build(1,top),m=read();
134     while(m--)
135     {
136         opt=read(),a=read()^cnt;
137         switch(opt)
138         {
139             case 1:cnt+=query(a);break;
140             case 2:b=read()^cnt;update(a,b%(lim+1));break;
141             case 3:
142                 b=read()^cnt;val[b]=read()^cnt;
143                 fa[b]=a;deep[b]=deep[a]+1;
144                 val[b]%=(lim+1);insert(a,b);break;
145         }
146     }
147 }
bzoj3729

而後是樹上刪邊遊戲……參考資料裏面有

在會了那個結論以後仍是比較裸的,dp一發求個方案數再轉機率就好了

有這樣一句話比較好「在博弈論中凡是等價的狀態都是能夠相互替換的」,正因如此咱們能夠把以前那一堆樹枝替換成一條「竹子」

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 #define N 110
 5 #define db double
 6 #define K 128
 7 int n;
 8 db f[N][K<<1],g[N][K<<1];
 9 bool vis[N][K<<1];
10 inline void init()
11 {
12     register int i,j,u,v,cnt=1;
13     vis[1][0]=1;g[1][0]=1;db tmp;
14     for(i=2;i<=100;++i)
15     {
16         for(u=0;u<i-1;++u)
17             if(vis[i-1][u])
18                 vis[i][u+1]=1,g[i][u+1]+=2*g[i-1][u];
19         for(j=1;j<i;++j)
20             for(u=0;u<j;++u)if(vis[j][u])
21                 for(v=0;v<i-j-1;++v)if(vis[i-j-1][v])
22                     vis[i][(u+1)^(v+1)]=1,g[i][(u+1)^(v+1)]+=g[j][u]*g[i-j-1][v];
23     }
24     for(i=2;i<=100;++i)
25     {
26         for(tmp=0,j=0;j<i;++j)tmp+=g[i][j];
27         for(j=0;j<i;++j)g[i][j]/=tmp;
28     }
29 }
30 int main()
31 {
32     // freopen("Ark.in","r",stdin);
33     register int i,j,k,a;
34     scanf("%d",&n),init(),f[0][0]=1;
35     for(i=1;i<=n;++i)
36         for(scanf("%d",&a),j=0;j<K;++j)
37             for(k=0;k<a;++k)
38                 f[i][j^k]+=f[i-1][j]*g[a][k];
39     printf("%.6f\n",1-f[n][0]);
40 
41 }
bzoj2688

以及Nimk遊戲,固然上面的資料裏面也是有的

這也很好的解釋了爲何普通的min遊戲是異或:

異或以後每一位的值是本來1的個數x%(1+1)以後的值

也就是說異或啦,有偶數個1是0,奇數個1是1

上面那個方法總結的博客裏介紹了證實……

彷佛我這樣扔資料不太負責任啊2333

咱們能夠和以前那道也是棋子游戲的模型參考一下……

而後咱們就發現這倆題都是那樣棋子轉nim的模型,不過這題是nimk而已

打就行了……拿組合數Dp一下方案數

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 #define N 10010
 5 #define N1 10000
 6 #define K 110
 7 #define mod 1000000007
 8 #define LL long long
 9 int bin[25],n,k,d;
10 LL f[17][N],fac[N],inv[N];
11 inline LL C(int a,int b){return a<b?0:(fac[a]*inv[b]%mod*inv[a-b]%mod);}
12 int main()
13 {
14     // freopen("Ark.in","r",stdin);
15     register int i,j,x,ge;
16     for(bin[0]=i=1;i<=20;++i)bin[i]=bin[i-1]<<1;
17     scanf("%d%d%d",&n,&k,&d);
18     for(fac[0]=fac[1]=1,i=2;i<=N1;++i)fac[i]=fac[i-1]*i%mod;
19     for(inv[0]=inv[1]=1,i=2;i<=N1;++i)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
20     for(i=3;i<=N1;++i)inv[i]=inv[i]*inv[i-1]%mod;
21     LL ans=C(n,k);
22     f[0][0]=1;
23     for(i=0;i<=15;++i)
24         for(j=0;j<=n-k;++j)
25         {
26             f[i+1][j]=(f[i+1][j]+f[i][j])%mod;
27             for(x=1,ge=(d+1)*bin[i];x*(d+1)<=k/2&&j+ge<=n-k;++x,ge+=bin[i]*(d+1))
28                 f[i+1][j+ge]=(f[i+1][j+ge]+f[i][j]*C(k/2,x*(d+1)))%mod;
29         }
30     for(j=0;j<=n-k;++j)
31         ans-=f[16][j]*C(n-j-k/2,k/2)%mod;
32     printf("%lld\n",(ans%mod+mod)%mod );
33 }
bzoj2281

而後最後來一道二分圖博弈……

關於二分圖博弈,一個特色就是咱們每一個點只能走一次

常常和棋盤黑白染色轉二分圖搞在一塊兒

必勝和必敗經常與二分圖的交錯軌和匹配邊聯繫在一塊兒:

nim遊戲中咱們對於2堆同樣的石子會模仿對手的操做,而後對於二分圖類博弈咱們從對手的點出發走匹配邊

也是在模仿對手啦……只有對方能走,咱們就能走他那個點出去的匹配邊,因此是資瓷的

而後常常要考慮是最大匹配的」必須點「仍是」非必須點「

前者能夠經過從最大匹配的非匹配點dfs獲得,後者能夠刪掉這個點,看看本身的匹配點還能不能找到匹配

而後當時我搞的時候看了這篇總結,寫的很不錯……

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 #define N 45
 5 #define G 1610
 6 #define K 1010
 7 int n,m,opt[K<<1],k,ans[K],top;
 8 int stcol,id[N][N],v[N][N],col[N][N],cnt;
 9 char str[N];
10 int e,adj[G],match[G];
11 struct edge{int zhong,next;}s[G<<2];
12 inline void add(int qi,int zhong)
13     {s[++e].zhong=zhong;s[e].next=adj[qi];adj[qi]=e;}
14 int vis[G],T;
15 bool del[G];
16 inline bool find(int rt)
17 {
18     if(del[rt])return false;
19     register int i,u,x;
20     for(i=adj[rt];i;i=s[i].next)
21         if(vis[u=s[i].zhong]!=T)
22         {
23             vis[u]=T;
24             if(del[u])continue;
25             if(!match[u]||find(match[u]))
26                 {match[rt]=u,match[u]=rt;return true;}
27         }
28     return false;
29 }
30 inline bool judge(int rt)
31 {
32     del[rt]=1;
33     if(!match[rt])return 0;
34     int tofind=match[rt];
35     ++T,match[rt]=match[tofind]=0;
36     return find(tofind)==0;
37 }
38 int main()
39 {
40     // freopen("Ark.in","r",stdin);
41     register int i,j,a,b;
42     scanf("%d%d",&n,&m);
43     for(i=1;i<=n;++i)
44         for(j=1;j<=m;++j)col[i][j]=((i+j)&1);
45     for(i=1;i<=n;++i)
46         for(scanf("%s",str+1),j=1;j<=m;++j)
47         {
48             if(str[j]=='.')a=i,b=j,stcol=col[i][j];
49             v[i][j]=(str[j]!='O');
50         }
51     for(i=1;i<=n;++i)
52         for(j=1;j<=m;++j)
53             if((v[i][j]&&col[i][j]==stcol)||(v[i][j]==0&&col[i][j]!=stcol))
54                 id[i][j]=++cnt;
55     for(i=1;i<=n;++i)
56         for(j=1;j<=m;++j)
57             if(id[i][j])
58             {
59                 if(id[i-1][j])add(id[i][j],id[i-1][j]);
60                 if(id[i+1][j])add(id[i][j],id[i+1][j]);
61                 if(id[i][j-1])add(id[i][j],id[i][j-1]);
62                 if(id[i][j+1])add(id[i][j],id[i][j+1]);
63             }
64     for(i=1;i<=cnt;++i)
65         if(!match[i])++T,find(i);
66     scanf("%d",&k);k<<=1;
67     for(i=1;i<=k;++i)
68         opt[i]=judge(id[a][b]),scanf("%d%d",&a,&b);
69     for(i=1;i<=k;i+=2)
70         if(opt[i]&&opt[i+1])ans[++top]=((i+1)>>1);
71     printf("%d\n",top);
72     for(i=1;i<=top;++i)printf("%d\n",ans[i]);
73 }
74 //二分圖類的博弈論?
75 //一個格子只能被訪問一次
bzoj2437

大概我如今學的東西就這些……博弈論真的是頗有趣的知識,一開始很怕這個……

可是如今發現這種博弈的思想頗有意思,雙方都要取最優策略,

而後要靈活的考慮sg函數的意義,模型的轉換,技巧的使用,固然還有背結論

但願你們學的開心……

啊累死了我要回去睡覺

相關文章
相關標籤/搜索