POJ 3710 Christmas Game(Tarjan+博弈SG函数)
2015-11-16 16:17
513 查看
【题目链接】
http://acm.hust.edu.cn/vjudge/problem/toListProblem.action#OJId=POJ&probNum=3710&title=&source=
【解题报告】
看过贾志豪的论文之后做的一道题。
有几个关键点:
tarjan算法判环,然后根据观察所得结论对环缩点。tarjan算法相关内容可以参见我的另一篇题解。
这里写下我的理解。
奇环可以通过去掉一条边变成两条长度相等的链,这是一个必败局面P,它的SG值为0.所以奇环是N局面。
当任意去掉一条边时,一定形成SG值为2k和2m的两条链,他们的异或值一定不为1.
所以由定义知,奇环的SG值为1.
相对的,先手在偶环里去掉一条边,链的长度一定为一奇一偶,,即偶环的所有子局面SG值都不为0,所以,偶环的SG值为0.
之后变成了这样一个模型:
有定理:
关于定理的证明可以参见论文。如果一遍不好理解的话可以多看几遍。
最后这道题目就被转化成了经典的NIM游戏。
附上我自己的代码,写的特别丑。轻拍。
【参考资料】
《组合游戏略述——浅谈SG游戏的若干拓展及变形》–贾志豪
《博弈类题目小结》——ACM-cxlove
http://blog.csdn.net/acm_cxlove/article/details/7854526
《POJ 2186 Popular Cows(强连通分量缩点,Tarjan算法)》–gungnir
http://blog.csdn.net/gungnir0711/article/details/49867467
【参考代码】
http://acm.hust.edu.cn/vjudge/problem/toListProblem.action#OJId=POJ&probNum=3710&title=&source=
【解题报告】
看过贾志豪的论文之后做的一道题。
有几个关键点:
1.如何处理树上的环
tarjan算法判环,然后根据观察所得结论对环缩点。tarjan算法相关内容可以参见我的另一篇题解。
这里写下我的理解。
奇环可以通过去掉一条边变成两条长度相等的链,这是一个必败局面P,它的SG值为0.所以奇环是N局面。
当任意去掉一条边时,一定形成SG值为2k和2m的两条链,他们的异或值一定不为1.
所以由定义知,奇环的SG值为1.
相对的,先手在偶环里去掉一条边,链的长度一定为一奇一偶,,即偶环的所有子局面SG值都不为0,所以,偶环的SG值为0.
之后变成了这样一个模型:
有定理:
叶子节点的SG值为0(P状态),中间节点的SG值为它所有t个叶子节点的SG值加一的异或值: SG(G)=(SG(G1')+1)^(SG(G2')+1)^ ... ^(SG(Gt')+1)
关于定理的证明可以参见论文。如果一遍不好理解的话可以多看几遍。
最后这道题目就被转化成了经典的NIM游戏。
附上我自己的代码,写的特别丑。轻拍。
【参考资料】
《组合游戏略述——浅谈SG游戏的若干拓展及变形》–贾志豪
《博弈类题目小结》——ACM-cxlove
http://blog.csdn.net/acm_cxlove/article/details/7854526
《POJ 2186 Popular Cows(强连通分量缩点,Tarjan算法)》–gungnir
http://blog.csdn.net/gungnir0711/article/details/49867467
【参考代码】
#include<cstdio> #include<iostream> #include<cstring> #include<stack> #include<cmath> using namespace std; const int maxn=1e4+50; const int maxm=5e4+50; int head[maxn],LOW[maxn],DFN[maxn],id[maxn],cnt[maxn]; int mp[110][110]; int vis_scc[maxn]; bool mark[maxn]; int time,scc; int n,m,k,state; stack<int>sta; struct edge_T{ int to,next; }edge[maxm]; void tarjan( int u, int pre ) { DFN[u]=LOW[u]=++time; sta.push(u); mark[u]=true; for( int k=head[u]; k!=-1; k=edge[k].next ) { int v=edge[k].to; if(v==pre)continue; if(!DFN[v] ) //v还没有时间戳 { tarjan(v,u); //给v打上时间戳 LOW[u]=min( LOW[u],LOW[v] ); } else if(mark[v] ) { LOW[u]=min( LOW[u],DFN[v] ); } } if(LOW[u]==DFN[u]) { ++scc; int v; do { cnt[scc]++; v=sta.top(); sta.pop(); id[v]=scc; mark[v]=false; }while(u!=v); } } int get_SG( int u , int pre ) { if(head[u]==-1)return 0; int ans=0; sta.push(u); for( int k=head[u]; k!=-1; k=edge[k].next ) { int v=edge[k].to; if(mp[u][v]>1) //偶数条重边就直接忽略它,不再继续搜索,因为一定是树叶(按照题意),并且SG值为0 { if(mp[u][v]&1)mp[u][v]=1; else continue; } if(v==pre)continue; if( (id[u]==id[v]) ) { if( vis_scc[id[u]] ) continue;//已经处理过这个scc if(cnt[id[u]]&1)ans^=1; //奇环 else ans^=0; //偶环 vis_scc[id[u]]=true; } else ans^=(get_SG(v,u)+1);//不是同一个强连通分量,继续搜索 } return ans; } void solve() { memset(mark,0,sizeof(mark));//tarjan的初始化 memset(DFN,0,sizeof(DFN)); memset(vis_scc,0,sizeof(vis_scc)); memset(cnt,0,sizeof(cnt)); time=0; scc=0; while(!sta.empty())sta.pop(); for( int i=1; i<=m; i++ ) if(!DFN[i]) tarjan(i,-1); memset(vis_scc,0,sizeof(vis_scc)); int t=get_SG(1,-1); //从根节点开始DFS求出SG值 state^=t; // cout<<"SG(1)= "<<t<<endl; } void add_edge( int L, int R, int O ) { edge[O].to=R; edge[O].next=head[L]; head[L]=O; } int main() { while(~scanf("%d",&n)) { state=0; while(n--) //n个subtree,每个求出根的SG值,然后求异或和 { scanf("%d%d",&m,&k); memset(head,-1,sizeof head); memset(mp,0,sizeof(mp)); for( int i=1; i<=k; i++ ) { int a,b; scanf("%d%d",&a,&b); add_edge( a,b,i*2-1 ); add_edge( b,a,i*2 ); mp[a][b]++; mp[b][a]++; } solve(); } // for( int i=1; i<=m; i++ )cout<<i<<" "<<id[i]<<endl; if(state)printf("Sally\n"); else printf("Harry\n"); } return 0; }
相关文章推荐
- taskmgr多开补丁
- Android shape的使用
- web安全性考虑的几方面
- 项目1.4--哈弗曼编码的算法验证
- 简单理解JSP(三)
- <1> go 继承
- 研发过程管理导图
- 复杂布局的ListView
- jquery前言
- VLC搭建RTSP服务器的过程
- Redis总结(一)Redis安装
- 站长不能一味发外链 而要构成网站整体外链建设战略
- 第十二周项目(1):图基本算法库
- 第9周项目3 稀疏矩阵的三元组表示的实现及应用
- 日志实时分析统计平台一 ----- 简介
- 简单理解JSP(二)
- os.path
- Android 侧滑菜单
- Fresco(展示图片)、EventBus(事件传递)
- netstat指令及端口