[HNOI2007]分裂游戏
題目描述
聰聰和睿睿最近迷上了一款叫做分裂的游戲。 該游戲的規則試: 共有 n 個瓶子, 標號為 0,1,2.....n-1, 第 i 個瓶子中裝有 p[i]顆巧克力豆,兩個人輪流取豆子,每一輪每人選擇 3 個瓶子。標號為 i,j,k, 并要保證 i < j , j < = k 且第 i 個瓶子中至少要有 1 顆巧克力豆,隨后這個人從第 i 個瓶子中拿走一顆豆 子并在 j,k 中各放入一粒豆子(j 可能等于 k) 。如果輪到某人而他無法按規則取豆子,那么他將輸 掉比賽。勝利者可以拿走所有的巧克力豆! 兩人最后決定由聰聰先取豆子,為了能夠得到最終的巧克力豆,聰聰自然希望贏得比賽。他思考 了一下,發現在有的情況下,先拿的人一定有辦法取勝,但是他不知道對于其他情況是否有必勝 策略,更不知道第一步該如何取。他決定偷偷請教聰明的你,希望你能告訴他,在給定每個瓶子 中的最初豆子數后是否能讓自己得到所有巧克力豆,他還希望你告訴他第一步該如何取,并且為 了必勝,第一步有多少種取法? 假定 1 < n < = 21,p[i] < = 10000
輸入輸出格式
輸入格式:
輸入文件第一行是一個整數t表示測試數據的組數,接下來為t組測試數據(t<=10)。每組測試數據的第一行是瓶子的個數n,接下來的一行有n個由空格隔開的非負整數,表示每個瓶子中的豆子數。
輸出格式:
對于每組測試數據,輸出包括兩行,第一行為用一個空格兩兩隔開的三個整數,表示要想贏得游戲,第一步應該選取的3個瓶子的編號i,j,k,如果有多組符合要求的解,那么輸出字典序最小的一組。如果無論如何都無法贏得游戲,那么輸出用一個空格兩兩隔開的三個-1。第二行表示要想確保贏得比賽,第一步有多少種不同的取法。
輸入輸出樣例
輸入樣例#1:
2
4
1 0 1 5000
3
0 0 1
輸出樣例#1:
0 2 3
1
-1 -1 -1
0
題解
剛剛學習了\(Multi\_SG\)游戲
然后還是煤油搞出來這道題
首先通過觀察我們可以發現當石子的個數為偶數個的時候后手操作可以對先手操作進行模仿
那么要描述這個局面就只需要考慮石子數量是奇數的石子堆了
這樣整個序列就可以看做是一個01序列
然后可以看出必敗態就是除了最后一個瓶子有豆子外其他的瓶子都沒有豆子了
由于整個數列可以看做是一個01數列
那么我們就只需要考慮一個石子的移動
這樣我們就可以設\(SG(x)\)表示在\(x\)處有一個石子,ta到必敗態的\(SG\)值
那么顯然\(SG(n)=0\)
那么我們就可以倒著推出每個位置的\(SG\)值
然后描述局面\(sit\)就是將所有是奇數個石子的堆的\(SG\)函數異或起來
題目讓求第一步的合法必勝方案
我們就枚舉三元組\((i,j,k),i<j,j\le k,i\)堆必須有石子
由于是必勝方案
所以走完這步以后就要讓對面進入必敗方案
所以就找\(sit\oplus SG(i)\oplus SG(j)\oplus SG(k)=0\)的三元組即可
代碼
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int M = 55 ; using namespace std ;inline int read() {char c = getchar() ; int x = 0 , w = 1 ;while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }return x*w ; }bool vis[M] ; int n , tmp , ans ; int A , B , C , val[M] , sg[M] ;int main() {int Case = read() ;while(Case --) {n = read() ; ans = 0 ; tmp = 0 ; A = B = C = 0 ;memset(sg , 0 , sizeof(sg)) ;for(int i = 1 ; i <= n ; i ++)val[i] = read() ;for(int i = n - 1 ; i >= 1 ; i --) {memset(vis , false , sizeof(vis)) ;for(int j = i + 1 ; j <= n ; j ++)for(int k = j ; k <= n ; k ++)vis[sg[j] ^ sg[k]] = true ;for(int j = 0 ; ; j ++) if(!vis[j]) {sg[i] = j ;break ;}}for(int i = 1 ; i <= n ; i ++)if(val[i] & 1)tmp ^= sg[i] ;for(int i = 1 ; i <= n ; i ++)if(val[i])for(int j = i + 1 ; j <= n ; j ++)for(int k = j ; k <= n ; k ++)if(!(tmp ^ sg[i] ^ sg[j] ^ sg[k])) {if(!ans) A = i , B = j , C = k ;++ ans ;}printf("%d %d %d\n%d\n",A - 1 , B - 1 , C - 1 , ans) ;}return 0 ; }轉載于:https://www.cnblogs.com/beretty/p/10747290.html
總結
以上是生活随笔為你收集整理的[HNOI2007]分裂游戏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第5天:基于类的视图与中间件
- 下一篇: UDF、UDAF、UDTF函数编写