闲来无事刷水题、简单博弈论专题、sg函数、洛谷
記
今天閑來無事,不想刷codeforces了,到洛谷提高組訓練營找幾道水題刷著玩玩(雖然自己早已過了打OI的年紀)~
簡單博弈論專題
P1199 三國游戲
這么考慮,由于電腦總是不能讓我搭配出當前能搭配出的最大的組合,也就是說我不管選哪個英雄,都不可能匹配出該英雄能搭配出的最大的默契值,那么我最大能搭配出的默契值就是所有英雄匹配中,次大默契值的最大值。
假設有a、b、c、da、b、c、d四位英雄
aa能匹配的最大值為aMaM次大值為amam
bb能匹配的最大值為bMbM次大值為bmbm
cc能匹配的最大值為cMcM次大值為cmcm
dd能匹配的最大值為dMdM次大值為dmdm
假設其中am>bm>cm>dmam>bm>cm>dm
那么我能得到的最大可能值也就是amam了。
下面證明,電腦能組合出的最大值一定比amam要小。
那么我一開始選擇aa,電腦一定會選擇與aa能形成aMaM的值的英雄,假設是dd。
然后我再選cc,這里假設c與a組合得到amc與a組合得到am。
那么電腦會選擇bb。
反證法開始了:
假設d與b形成的默契值t>am>dmd與b形成的默契值t>am>dm
而d與a形成的默契值為aM>am>dmd與a形成的默契值為aM>am>dm
這樣的話dmdm不可能是dd能匹配到的次大值,矛盾。
所以t<amt<am更大的數。
隨后我們只需要破壞掉電腦構造成最大的組合就可以了。
P1288 取數游戲II
如果出發點靠著0邊,那么只能往一個方向走,并且走過的邊的值一定要減到0,不然對面會逆過來,然后把邊減到0,這樣我方就輸了。
不靠0邊的時候,如果沿著某個方向走會贏,那么就把沿路經過的邊設置為0。
如果沿著那個方向走都不會贏,你任取一個方向走的時候不把邊設置為0,對方在下一次走的時候也會繼續沿著這個方向,并把下一條邊設置成0,你還是要一直沿著這個方向走。
所以不管怎么樣,經過的邊都會變成0,也就是說一旦選定方向了,就只能一直走。
所以結果很固定,你只要把2個方向都老老實實沿著走下去,有一種情況能贏就能贏,否則就不能贏。
P1290 歐幾里德的游戲
典型的輾轉相除法。
假設這時候狀態是(a,b)滿足a>b(a,b)滿足a>b
如果[a/b]=1[a/b]=1那么(a,b)(a,b)與(b,a%b)(b,a%b)的狀態是相反的。
如果[a/b]=k>1[a/b]=k>1那么就是必勝的,因為如果(b,a%b)(b,a%b)是必敗態,那么a直接剪去k個b,否則的話,a剪去k-1個b。很簡單是不是。
P2148 [SDOI2009]E&D
山東的省選題,中規中矩。
由于多個組是獨立游戲,所以Nim博弈,直接求出所有組的sg函數,然后異或就可以了,如果異或值為0則必敗,否則必勝。
由于每個二元組(a,b)(a,b)的范圍都是1e91e9,這樣的話,sg函數的復雜度太大了,所以要想想辦法。
方法就是:打表找規律!2333
規律如下:
P1247 取火柴游戲
簡單的不像話,跟上面的題難度一樣,方法也一樣,求sg函數,然后異或。
不同的地方是,這個要求第一步的方案。
在必勝態的時候,異或得到的值為ans。
我們第一步要做的就是拿掉一堆中的某些火柴,然后使異或值變為0。
假設我們要拿掉第i堆的某些火柴,那么這一堆應該剩下的火柴的數目就是ansans xor n[i]n[i],拿走的火柴數目就是n[i]?n[i]? (ansans xor n[i]n[i])。
從小到達枚舉堆數,找一個合法的情況就好了。
P2575 高手過招
這到也是sg函數?異或。
不得不說sg函數是博弈論的大殺器呀。
求sg函數的方法就不說了,注意的點:
- 這題要用到位運算,壓縮狀態,不然時間、空間可能不夠。
- 開O2!!!
代碼
#include <iostream> #include <cstdio> #include <set> #include <cstring> #include <unordered_map> using namespace std; const int maxn = 2e6; int T,n,m; int sg[maxn]; int grundy(int status){if(sg[status] != -1)return sg[status];unordered_map<int,int> mp;for(int i = 19;i > 0;--i){if(!(status&(1<<i))) continue;int j = i-1;for(;j >= 0;--j)if(!(status&(1<<j)))break;if(j == -1) continue;mp[grundy(status ^ ((1<<i) + (1<<j)))] = 1;;}for(int i = 0;;i++){if(!mp[i])return sg[status] = i;} } int main(){cin>>T;memset(sg,-1,sizeof(sg));while(T--){scanf("%d",&n);int ans = 0;for(int i = 1;i <= n;++i){scanf("%d",&m);int status = 0;for(int j = 1;j <= m;++j){int p;scanf("%d",&p);status |= 1<<(20-p);}ans ^= grundy(status);}if(ans) puts("YES");else puts("NO");}return 0; }總結
以上是生活随笔為你收集整理的闲来无事刷水题、简单博弈论专题、sg函数、洛谷的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 易点天下获阿里云代理授牌 打通“营销+云
- 下一篇: 小米14外观正式公布!6.36吋超窄直屏