zoukankan      html  css  js  c++  java
  • 博弈论和SG函数

    emmmmm,是因为在一次训练赛中看到了一道题, 然后就去学了一遍单独发出来把
    image
    image
    在nim博弈的定义和证明上算法进阶讲的还是挺详细的, 上道题
    洛谷P5675 [GZOI2017]取石子游戏
    根据以上定义, 当Alice取完石子后的异或值不为0, 那么一定是一种必败的情况, 假如所取第一堆的数量为(a_i), 而其他的石子的异或值大≥(a_i), 那么无论Alice怎么取, 都不会使异或值为0, 我们再看数据范围, 每堆石子的数量最多是200, 不超过(2^8), 那么我们可以枚举出第一堆所取哪一堆和其他堆石子的状态,设f[i][j]表示,前第i堆石子的状态是j的方案数,当然, 要求n遍, 因为要枚举每一堆作为第一堆, 这样, 这道题就完美解决了

    点击查看代码
    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int mod = 1e9 + 7;
    int n, ans = 0, a[210], f[210][260];
    
    int main() {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    	for (int i = 1; i <= n; ++i) {
    		memset(f, 0, sizeof(f));
    		f[0][0] = 1;
    		for (int j = 1; j <= n; ++j) {
    			for (int k = 0; k < 256; ++k) {
    				if (i == j) f[j][k] = f[j - 1][k];
    				else f[j][k] = (f[j - 1][k] + f[j - 1][k ^ a[j]]) % mod;
    			}
    		}
    		for (int j = a[i]; j < 256; ++j) 
    			ans = (ans + f[n][j]) % mod;
    	}
    	cout << ans << endl; 
    	return 0;
    }
    

    我们继续看另一类博弈论游戏:
    image

    接下来我们引进SG函数
    image

    image

    所以, 我们如果看到博弈论游戏, 可以先算出终止情况并赋值为0, 然后一步步的求出SG函数, 判断异或值, 注意的是一种情况的SG函数是根据所有子情况来算出的, 并且一定要看出等效的情况, 有时候abc和def其实是一样的, 这样会很节省时间
    上训练赛的题

    K. Alice and Bob-2
    首先全为0的话肯定是必败的情况, 不为0的情况下我们就根据这两个条件暴力的去取数, 当然也必须要记忆化(PS: 在全为0的情况下, 我忘了return SG[x] = 0, 会T, 加上这句话后跑的飞快, 并且每次递归的时候记得排序, 因为是一种等效的情况, 否则会很浪费时间)

    点击查看代码
    #include <bits/stdc++.h>
    
    using namespace std;
    
    int t, n; 
    char ch[50];
    map < vector < int > , int > SG; 
    
    inline bool check(vector < int > x) {
    	for (int i = 0; i <= 25; ++i) 
    		if (x[i]) return false;
    	return true;
    }
    
    inline int Find(vector < int > x) {
    	if (SG.find(x) != SG.end()) return SG[x];
    	if (check(x)) return SG[x] = 0;
    	vector < int > a;
    	for (int i = 0; i <= 25; ++i) {
    		if (x[i]) {
    			vector < int > vec = x;
    			--vec[i];
    			sort(vec.begin(), vec.end());
    			a.push_back(Find(vec));
    		}
    	}
    	for (int i = 0; i <= 25; ++i) {
    		for (int j = 0; j <= 25; ++j) {
    			if (i == j) continue;
    			if (x[i] && x[j]) {
    				vector < int > vec = x;
    				--vec[i], --vec[j];
    				sort(vec.begin(), vec.end());
    				a.push_back(Find(vec));
    			}
    		} 
    	}
    	sort(a.begin(), a.end());
    	for (int i = 0; ; ++i) {
    		bool flag = false;
    		for (int j = 0; j < a.size(); ++j) {
    			if (i == a[j]) {
    				flag = true;
    				break;
    			} else if (a[j] > i) break;
    		} 
    		if (!flag) return SG[x] = i;
    	} 
    }
    
    int main() {
    	scanf("%d", &t);
    	while (t--) {
    		scanf("%d", &n);
    		int ans = 0;
    		while (n--) {
    			vector < int > v;
    			for (int i = 0; i <= 25; ++i) v.push_back(0);
    			scanf("%s", ch + 1);
    			int len = strlen(ch + 1);
    			for (int i = 1; i <= len; ++i) ++v[ch[i] - 'a'];
    			sort(v.begin(), v.end());
    			ans ^= Find(v);
    		}
    		if (ans > 0) puts("Alice");
    		else puts("Bob");
    	}
    	return 0;
    } 
    

    洛谷P7395 弹珠游戏(2021 CoE-I C)
    总共只有16个棋子, 考虑状压, 并且我们把斜着翻转过来, 变成这个4*4的矩阵, 枚举每一步, 记搜即可

    点击查看代码
    #include <bits/stdc++.h>
    
    using namespace std; 
    
    typedef long long ll;
    const int mod = 998244353;
    const int ans = (1 << 16) - 1;
    const int N = (1 << 16) + 10;
    const int M = 5e2 + 10;
    
    template < typename T > inline void read(T &x) {
    	x = 0; T ff = 1, ch = getchar();
    	while (!isdigit(ch)) {
    		if (ch == '-') ff = -1;
    		ch = getchar();
    	} 
    	while (isdigit(ch)) {
    		x = (x << 1) + (x << 3) + (ch ^ 48);
    		ch = getchar();
    	}
    	x *= ff;
    }
    
    int t, SG[N], a[20] = {0, 3, 2, 7, 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 8, 13, 12};
    
    inline int find(int x) {
    //	printf("x = %d
    ", x);
    	if (SG[x]) return SG[x];
    	if (x == ans) return 0;
    	vector < int > v;
    	// 单个 
    	for (int i = 1; i <= 4; ++i) {
    		for (int j = 1; j <= 4; ++j) {
    			int k = (i - 1) * 4 + j - 1;
    			if (x & (1 << k)) {
    				int y = x - (1 << k);
    				v.push_back(find(y));
    			}
    		}
    	}
    	// 横着两个,
    	for (int i = 1; i <= 4; ++i) {
    		for (int j = 1; j <= 3; ++j) {
    			int k = (i - 1) * 4 + j - 1;
    			int k2 = k + 1;
    			if ((x & (1 << k)) && (x & (1 << k2))) {
    				int y = x - (1 << k) - (1 << k2);
    				v.push_back(find(y));
    			}
    		}
    	} 
    	// 竖着两个
    	for (int i = 1; i <= 3; ++i) {
    		for (int j = 1; j <= 4; ++j) {
    			int k = (i - 1) * 4 + j - 1;
    			int k2 = k + 4;
    			if ((x & (1 << k)) && (x & (1 << k2))) {
    				int y = x - (1 << k) - (1 << k2);
    				v.push_back(find(y));
    			}
    		}
    	} 
    	
    	//右下斜两个
    	for (int i = 1; i <= 3; ++i) {
    		for (int j = 1; j <= 3; ++j) {
    			int k = (i - 1) * 4 + j - 1;
    			int k2 = k + 5;
    			if ((x & (1 << k)) && (x & (1 << k2))) {
    				int y = x - (1 << k) - (1 << k2);
    				v.push_back(find(y));
    			}
    		} 
    	} 
    	
    	// 右上斜两个
    	for (int i = 2; i <= 4; ++i) {
    		for (int j = 1; j <= 3; ++j) {
    			int k = (i - 1) * 4 + j - 1;
    			int k2 = k - 3;
    			if ((x & (1 << k)) && (x & (1 << k2))) {
    //				printf("i = %d j = %d
    ", i, j);
    				int y = x - (1 << k) - (1 << k2);
    				v.push_back(find(y));
    			}
    		}
    	} 
    	
    	//横着3个
    	for (int i = 1; i <= 4; ++i) {
    		for (int j = 1; j <= 2; ++j) {
    			int k = (i - 1) * 4 + j - 1;
    			int k2 = k + 1;
    			int k3 = k + 2;
    			if ((x & (1 << k)) && (x & (1 << k2)) && (x & (1 << k3))) {
    				int y = x - (1 << k) - (1 << k2) - (1 << k3);
    				v.push_back(find(y));
    			}
    		}
    	} 
    	
    	//竖着3个
    	for (int i = 1; i <= 2; ++i) {
    		for (int j = 1; j <= 4; ++j) {
    			int k = (i - 1) * 4 + j - 1;
    			int k2 = k + 4;
    			int k3 = k + 8;
    			if ((x & (1 << k)) && (x & (1 << k2)) && (x & (1 << k3))) {
    				int y = x - (1 << k) - (1 << k2) - (1 << k3);
    				v.push_back(find(y));
    			}
    		}
    	} 
    	
    	//右下斜3个
    	for (int i = 1; i <= 2; ++i) {
    		for (int j = 1; j <= 2; ++j) {
    			int k = (i - 1) * 4 + j - 1;
    			int k2 = k + 5;
    			int k3 = k + 10;
    			if ((x & (1 << k)) && (x & (1 << k2)) && (x & (1 << k3))) {
    				int y = x - (1 << k) - (1 << k2) - (1 << k3);
    				v.push_back(find(y));
    			}
    		}
    	} 
    	
    	//右上斜3个
    	for (int i = 3; i <= 4; ++i) {
    		for (int j = 1; j <= 2; ++j) {
    			int k = (i - 1) * 4 + j - 1;
    			int k2 = k - 3;
    			int k3 = k - 6;
    			if ((x & (1 << k)) && (x & (1 << k2)) && (x & (1 << k3))) {
    				int y = x - (1 << k) - (1 << k2) - (1 << k3);
    				v.push_back(find(y));
    			}
    		}
    	} 
    	for (int i = 0; ; ++i) {
    		bool flag = false;
    		for (int j = 0; j < v.size(); ++j) {
    			int y = v[j];
    			if (y == i) {
    				flag = true;
    				break;
    			}
    		}
    		if (!flag) return SG[x] = i;
    	}
    }
    
    int main() {
    	read(t); 
    	while (t--) {
    		int cnt = 0;
    		char ch = getchar();
    		int i = 1; 
    		while (i <= 16) {
    			ch = getchar();
    			if (ch == '*' || ch == '.') {
    				if (ch == '.') cnt |= (1 << a[i]);
    				++i;
    			}
    		}
    		if (find(cnt) > 0) puts("Possible.");
    		else puts("Impossible."); 
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    HCNA配置ssh远程登陆
    HCNA配置telnet远程管理
    HCNA配置接口IP地址
    HCNA配置console线路密码aaa认证
    HCNA配置console线路密码password认证
    CentOS6.9上安装FreeSWITCH1.6.19
    vos语音业务规范操作承诺函
    vos对接时业务确认及信息安全责任承诺书
    Ubuntu 16下安装64位谷歌Chrome浏览器
    Python更新pip出现错误解决方法
  • 原文地址:https://www.cnblogs.com/AK-ls/p/15468482.html
Copyright © 2011-2022 走看看