组合博弈学习笔记
組合博弈學習筆記
說在前邊
HDU1527
搜索時發現,必敗態的數對貌似有規律,首先他們的大小兩個數的差值是逐個增加的。然后,差分打表,發現差值為1或者2.實在找不到規律了,OEIS了一發,是個黃金分割,學到了。
//**黃金分割** //**a(n) = floor(n*phi), where phi = (1+sqrt(5))/2 //**1,3,4,6,8,9,11,12,14,16,17,19,21,22,24,25,27,29,30,32,33,35,37 //**特點: 在i附近,貌似有規律地浮動 #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #define rep(i,a,b) for(int i=a;i<=b;++i) #define per(i,a,b) for(int i=a;i>=b;--i) #define pb push_back typedef long long ll; using namespace std; bool f[1101][1011],vis[1112][1101]; int dfs(int x,int y) {if(x==0&&y==0) return 0;if(vis[x][y]) return f[x][y];vis[x][y]=vis[y][x]=1;rep(i,1,x) if(!dfs(x-i,y)) return f[x][y]=f[y][x]=1;rep(i,1,y) if(!dfs(x,y-i)) return f[x][y]=f[y][x]=1;rep(i,1,min(x,y)) if(!dfs(x-i,y-i)) return f[x][y]=f[y][x]=1;return f[x][y]=f[y][x]=0; } vector<int> v; int main() { //** // rep(i,1,550) rep(j,1,i) { // if(!dfs(i,j))printf("(%d,%d) = %d\n",i,j,dfs(i,j)),v.pb(j);//**第i對數之間差i // } // cout << (sqrt(5)+1)/2.0 << endl; // rep(i,0,(int)v.size()-1) { // printf("%d, ",v[i]); // }puts(""); //**ll a,b;while(scanf("%lld%lld",&a,&b) != EOF) {if(a>b) swap(a,b);ll t = b-a;ll ta = floor((sqrt(5.0)+1.0)*t/2.0);if(ta == a) puts("0");else puts("1");}return 0; }HDU1760
直接搜索即可,hash去重直接用map,hash二進制壓位。
#include <cstdio> #include <algorithm> #include <cstring> #include <map> #define rep(i,a,b) for(int i=a;i<=b;++i) #define per(i,a,b) for(int i=a;i>=b;--i) typedef unsigned long long ull; const int N = 55; const int mod = 1000007; using namespace std; int n,m; map<ull,bool> s; char mp[N][N]; bool ck(int x,int y) {if(x>=1&&x+1<=n&&y>=1&&y+1<=m) {if(mp[x][y]=='0'&&mp[x][y+1]=='0'&&mp[x+1][y]=='0'&&mp[x+1][y+1]=='0')return 1;return 0;}return 0; } bool ack() {rep(i,1,n-1)rep(j,1,m-1)if(ck(i,j))return 1;return 0; } void put(int x,int y) {mp[x][y] = mp[x+1][y] = mp[x][y+1] = mp[x+1][y+1] = '1'; } void del(int x,int y) {mp[x][y] = mp[x+1][y] = mp[x][y+1] = mp[x+1][y+1] = '0'; } ull HS() { //hash 好好寫ull hs = 0;rep(i,1,n) rep(j,1,m)if(mp[i][j]=='0')hs |= (1LL<<((i-1LL)*m+j-1LL));return hs; } int dfs() {ull hs = HS();if(s.find(hs)!=s.end()) return s[hs];if(!ack()) return s[hs]=0;int ans = 0;rep(i,1,n-1)rep(j,1,m-1) if(ck(i,j)){put(i,j);if(!dfs()) ans |= 1;del(i,j);}return s[hs]=ans; } int main() {while(scanf("%d%d",&n,&m) != EOF) {rep(i,1,n) scanf(" %s",mp[i]+1);s.clear();if(dfs())puts("Yes");else puts("No");}return 0; }HDU1847
直接預處理sg函數即可
#include <cstdio> #include <cstring> #include <algorithm> #define rep(i,a,b) for(int i=a;i<=b;++i) #define per(i,a,b) for(int i=a;i>=b;--i) typedef long long ll; const int N = 1010; using namespace std; int n, sg[N]; bool vis[N]; void init() {sg[0] = 0;rep(s,1,1000) {memset(vis,0,sizeof(vis));rep(i,0,10)if((s>=(1<<i)))vis[sg[s - (1<<i)]] = 1;rep(i,0,1000) if(!vis[i]) {sg[s] = i;break;}} } int main() {init();while(scanf("%d",&n)!=EOF) {if(sg[n] == 0) puts("Cici");else puts("Kiki");}return 0; }HDU2516
記憶化搜索必敗態,必勝態,發現滿足斐波那契數列。之前的dp寫法我自己都沒看懂。。。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define rep(i,a,b) for(int i=a;i<=b;++i) #define per(i,a,b) for(int i=a;i>=b;--i) #define pb push_back typedef long long ll; using namespace std;//*** int dfs(int n,int x) {if(n==0)return 0;rep(i,1,min(2*x,n)) if(!dfs(n-i,i)) return 1;return 0; } int cal(int n) {rep(i,1,n-1) if(!dfs(n-i,i)) return 1;return 0; } void _init(int n) {rep(i,2,n) if(!cal(i)) printf("%d ",i);puts(""); } //***vector<ll> v; void init() {ll f0 = 1, f1 = 1, f2 = 2;while(f2<(1LL<<32LL)) {v.pb(f2);f0 = f1; f1 = f2; f2 = f1 + f0;} // for(auto x: v)printf("%lld ",x);puts(""); } int main() {//**//_init(50);//**init();ll n;while(scanf("%lld",&n),n) {if(*lower_bound(v.begin(),v.end(),n)==n)puts("Second win");else puts("First win");}return 0; }HDU3951
直接暴力求一根鏈情況下的sg函數,然后發現長度大于1必勝。環的情況就很容易了。還有一種思路是對稱的取硬幣可以證明后手必勝。
#include <cstdio> #include <cstring> #include <algorithm> #define rep(i,a,b) for(int i=a;i<=b;++i) #define per(i,a,b) for(int i=a;i>=b;--i) typedef long long ll; const int N = 1e3 + 7; using namespace std; int sg[N],vis[N]; void init(int K) {sg[0] = 0;rep(s,1,100) {memset(vis,0,sizeof(vis));rep(k,1,K) rep(i,1,s) {if(s-(i+k-1)>=0) {vis[sg[i-1]^sg[s-(i+k-1)]]=s;}}rep(i,0,100) if(vis[i]!=s){sg[s]=i;break;}}rep(s,1,100)printf("%d ",sg[s]);puts(""); } int main() {//**//rep(i,1,10)init(i);//對于一根鏈//當k=1時,奇數必勝//當k大于1時,大于0就必勝//**int T,C=0;scanf("%d",&T);while(T--) {ll n; int K;scanf("%lld%d",&n,&K);printf("Case %d: ",++C);if(K==1) {if((n-1)%2==1)puts("second");else puts("first");}else {if(n<=K) puts("first");else puts("second");}}return 0; }HDU4559
將格子拆為 2×i 的格子與 1×1 的格子的組合,把他們分別處理sg值然后異或起來。對于 1×1 的格子,sg值就是1,對于2*i的格子,可以直接求sg值,具體寫法見代碼。
#include <cstdio> #include <cstring> #include <algorithm> #define rep(i,a,b) for(int i=a;i<=b;++i) #define per(i,a,b) for(int i=a;i>=b;--i) #define pb push_back typedef long long ll; const int N = 4750; using namespace std; int T,C=0,n,m,sg[N],vis[N],num[N]; void init() {sg[0] = sg[1] = 0;rep(s,2,4747) {rep(i,1,s) {int l = i-1, r = s-i;vis[sg[l]^1^sg[r]]=s; // 1*1if(i+1<=s) {l=i-1; r=s-(i+1);vis[sg[l]^sg[r]]=s; // 2*2}}rep(i,0,4747) if(vis[i]!=s){sg[s] = i; break;}} } int solve() {int t0, t1, ans;t0=t1=ans=0;rep(i,1,n) {if(num[i] == 1) t1^=1;if(num[i] == 0) ++t0;else {ans^=sg[t0];t0 = 0;}}(ans^=sg[t0])^=t1;return ans; } int main() {init();scanf("%d",&T);while(T--) {scanf("%d%d",&n,&m);rep(i,0,n) num[i]=vis[i]=0;rep(i,1,m) {int x,y;scanf("%d%d",&x,&y);++num[y];}printf("Case %d: ",++C);if(solve()) puts("Alice");else puts("Bob");}return 0; }HDU4642
每次反轉一個位置右下角一定會翻轉一次,如果Alice將它反轉為0,則Bob一定會將它反轉為1,則最后一定是Alice贏。反之同理。所以直接判最后一位是0還是1即可
#include <iostream> #include <map> #include <cstring> #include <cstdio> #include <algorithm> #define rep(i,a,b) for(int i=a;i<=b;++i) #define per(i,a,b) for(int i=a;i>=b;--i) #define pb push_back typedef long long ll; using namespace std; int n,m; int main() {int T;scanf("%d",&T);while(T--) {scanf("%d%d",&n,&m);int x;rep(i,1,n)rep(j,1,m) scanf("%d",&x);if(x==1) puts("Alice");else puts("Bob");}return 0; }HDU5963
與上一題思路基本一致,子節點的修改都會對根節點上的對應邊進行反轉,所以直接判那條邊是否為1即可,多條邊,直接判斷奇偶。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <map> #define rep(i,a,b) for(int i=a;i<=b;++i) #define per(i,a,b) for(int i=a;i>=b;--i) #define MP make_pair #define PII pair<int,int> typedef long long ll; typedef unsigned long long ull; using namespace std; map<PII,int> M; int n,m,d[44444]; int main() {int T;scanf("%d",&T);while(T--) {scanf("%d%d",&n,&m);rep(i,1,n) d[i]=0; M.clear();rep(i,1,n-1) {int x,y,z;scanf("%d%d%d",&x,&y,&z);d[x]+=z;d[y]+=z;if(x > y) swap(x,y);M[MP(x,y)] = z;}rep(i,1,m) {int opt,x,y,z;scanf("%d",&opt);if(opt) {scanf("%d%d%d",&x,&y,&z);if(x>y)swap(x,y);int t = M[MP(x,y)];d[x]+=z; d[y]+=z;d[x]-=t; d[y]-=t;M[MP(x,y)] = z;}else {int x;scanf("%d",&x);if(d[x]%2)puts("Girls win!");else puts("Boys win!");}}}return 0; }轉載于:https://www.cnblogs.com/RRRR-wys/p/9404862.html
總結
- 上一篇: 地图概论和地形图的基本架构地图概论和地形
- 下一篇: Educational Codeforc