HDU3544 Alice's Game && POJ 2960 S-Nim(SG函数)
题意:
有一块xi*Yi的矩形巧克力,Alice只允许垂直分割巧克力,Bob只允许水平分割巧克力。具体来说,对于Alice,一块巧克力X i * Y i,只能分解成a * Y i和b * Y i其中a + b = X i和a, b > 0。对于Bob,一块巧克力X i * Y i,只能分解成X i * a和X i * b其中a + b = Y i和a ,b > 0。(每次切割只能以整数单位来切,例如一个宽为3的巧克力,你垂直切只能切成一个1,2而不能切成两个1.5)
谁最后不能操作了,谁就输了
题解:
根据题意我们只需要找到在给出的巧克力中他们两个人能走的最大步数,然后对比一下就可以了;但是另一个人的步数是和前一个人个操作有关
例如一个4*4的巧克力,如果每一次Alice都以1来切割(第一次1 3,第二次1 1 2,第三次1 1 1 1),这样的话Bob每一次切割宽为1的巧克力的话,他一共能走3*4=12步。但是如果每次Alice按照总宽度的一半来切割的话,那么Alice还是走4步(先切成2 2,然后再找每一个2来切),但是Bob就只能走2步
而且Alice肯定想让Bob走最小步数,因为Bob步数越大Alice就越难赢,所以每次Alice按照总宽度一半来切就是最优了
代码:
1 //参考:https://blog.csdn.net/qq_34374664/article/details/52959986 2 #include <iostream> 3 4 #include <cstdio> 5 6 #include <cstring> 7 8 #include <algorithm> 9 10 using namespace std; 11 12 const int maxn = 1e9 + 7; 13 14 int main() 15 16 { 17 18 int x, y, n, t, Case = 0; 19 20 scanf("%d", &t); 21 22 while(t--) 23 24 { 25 26 long long ansx = 0, ansy = 0; 27 28 scanf("%d", &n); 29 30 for(int i = 1; i <= n; i++) 31 32 { 33 34 scanf("%d%d", &x, &y); 35 36 while(x > 1 && y > 1) 37 38 { 39 40 x /= 2; 41 42 y /= 2; 43 44 ansx++; 45 46 ansy++; 47 48 } 49 50 if(x == 1) ansy += y - 1; 51 52 if(y == 1) ansx += x - 1; 53 54 } 55 56 if(ansx <= ansy) printf("Case %d: Bob\n", ++Case); 57 58 else printf("Case %d: Alice\n", ++Case); 59 60 61 62 } 63 64 65 66 67 68 return 0; 69 70 }
POJ 2960 S-Nim题意:
给你n堆石子,你每次只能取一定数量的石子,这个一定数量每个样例第一行就会输入;谁最后不能取石子谁就输了
题解:
很明显的SG函数,把第一个样例讲一下
2 2 5 //第一个数是k,后面输入k个数,每个数就是限制你每次只能取多少石子
3 //下面有多少行询问
2 5 12 //第一个数就是有多少堆石子,后面就是每一堆石子的数量
3 2 4 7
4 2 3 7 12
对于5 12 这两堆石子我们可以向尼姆博弈一样先处理一堆石子,之后再让它们相互异或
SG(0)=0 //初始化
SG(1)=0
SG(2)=mex{SG(0)}=1
SG(3)=mex{SG(1)}=1
SG(4)=mex{SG(2)}=0
SG(5)=mex{SG(0),SG(3)}=2
SG(6)=mex{SG(1),SG(4)}=1
SG(7)=mex{SG(2),SG(5)}=0
SG(8)=mex{SG(6),SG(3)}=0
SG(9)=mex{SG(7),SG(4)}=1
SG(10)=mex{SG(8),SG(5)}=1
SG(11)=mex{SG(9),SG(6)}=0
SG(12)=mex{SG(10),SG(7)}=2
所以两堆石子的结果就是SG(5)^SG(12)=0,所以这个时候就输了
那么肯定是每一组样例先打表对SG函数预处理
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <cstring> 5 #include <algorithm> 6 using namespace std; 7 #pragma comment(linker, "/STACK:102400000,102400000") 8 #define ls i<<1 9 #define rs ls | 1 10 #define mid ((ll+rr)>>1) 11 #define pii pair<int,int> 12 #define MP make_pair 13 typedef long long LL; 14 const long long INF = 1e18+1LL; 15 const double Pi = acos(-1.0); 16 const int N = 5e5+10, M = 2e5+20, mod = 1e9+7, inf = 2e9; 17 18 int k,sg[N],s[N],vis[N]; 19 char A[N]; 20 int main() { 21 while(scanf("%d",&k)!=EOF) { 22 if(k == 0) break; 23 for(int i = 1; i <= k; ++i) scanf("%d",&s[i]); 24 sg[0] = 0; 25 for(int i = 1; i <= 10000; ++i) { //预处理打表找出SG的值 26 for(int j = 0; j <= 100; ++j) vis[j] = 0; 27 for(int j = 1; j <= k; ++j) { 28 if(i >= s[j] && sg[i - s[j]] <= 100) vis[sg[i - s[j]]] = 1; //这一步就是判断从这个点都能到哪 29 } 30 for(int j = 0; j <= 100; ++j) { //这一步相当于找不在mex中最小的值 31 if(!vis[j]) { 32 sg[i] = j; 33 break; 34 } 35 } 36 } 37 int q,cnt = 0; 38 scanf("%d",&q); 39 while(q--) { 40 int x,y,ans = 0; 41 scanf("%d",&x); 42 while(x--) { 43 scanf("%d",&y); 44 ans ^= sg[y]; //得到每一堆石子的SG值之后再异或处理就可以了 45 } 46 if(ans) printf("W"); 47 else printf("L"); 48 } 49 printf("\n"); 50 } 51 return 0; 52 }