您的位置:首页 > 产品设计 > UI/UE

SGU 520 Fire in the Country 博弈SG函数(或者YY一下)

2014-09-03 11:44 369 查看
题目大意:

现在给出一个有着n个顶点m条边的图,边为双向边, 现在两个人轮流操作机器人,机器人从点1开始走,两个人轮流控制机器人走向与当前点相连的点,两个人每次操作机器人走的同时,火势将会向外蔓延,每操作一次之后火势就向与当前着火点相连的点蔓延,机器人在谁操作之后被火烧到谁就输,比如样例:

Nikolay先手从1走到3,当天晚上1着火,然后Vladimir只能从3走到1,当天晚上1是着火点所以Vladimir输了,输出Vladimir

大致思路:

这题比赛的时候想的是一个YY的做法:

我定义P点:一个点是P点当且仅当谁控制机器人走到这个点谁就赢

相反的是N点:一个点是N点表示谁控制机器人走到这个点谁就输

由于火势是不停蔓延的,那么控制机器人行动时不能回头,这样即使图中有环,也不可能往回走,可以先BFS遍历一下找到点的层级( 即到达该点的步数 ) 这样我们就只能控制机器人从层级低的点走到层级高的点,这样原图可以变成一个无环有向图,图的边来自于原图中层级低的点指向层级高的点且在原图中连通

我们考虑父亲结点root和子节点son[ i ] ( 这里就是拓扑序的意思,方编写我就当做树的层级吧..虽然可能不是树...) ,当父亲节点没有子节点时,一定是 P 点,因为下一个人已经无路可走了

当父亲节点有子节点时,如果子节点中有P点,那么父亲节点是N点( 博弈中的小策略 )也很容易明白因为代打这个父亲节点后,下一个人一定会选择走到P点这样走到这个父亲节点的人就输了,也就是说只需要判断父亲节点的子节点中有无P点即可判断父亲节点是P点还是N点

这样dfs一下找到节点1的值是P还是N即可,是N则Vladimir赔钱,否则Nikolay赔钱

比赛之后学了一些图上的博弈,发现这题可以用Sprague-Grundy函数来解和之前一样BFS建好新的无环有向图之后,用dfs迭代求SG函数的值

当SG[ 1 ] == 0 时,Nikolay赔钱,SG[ 1 ] != 0时,Vladimir赔钱

这里附上SG函数解法的代码(相比上一种比赛时YY出来的方案感觉还是好一点)

题解写一半发现CSDN维护了..我擦...

Result  :  Accepted     Memory  :  322 KB     Time  :  15 ms

/*
* Author: Gatevin
* Created Time: 2014/9/2 22:10:56
* File Name: C.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define pb(x) push_back(x)
#define clr(x) memset(x, 0, sizeof(x))
int n,m;
int deep[1010];//到达对应的点需要走的步数
bool vis[1010];//用了两次,都是用来记忆化
vector <int> G[1010];//原图
vector <int> F[1010];//删边之后的博弈求SG函数用图
queue <int> q;
int SG[1010];

void bfs()
{
clr(vis);
vis[1] = 1;
q.push(1);
while(!q.empty())
{
for(unsigned int i = 0; i < G[q.front()].size(); i++)
{
if(!vis[G[q.front()][i]])
{
deep[G[q.front()][i]] = deep[q.front()] + 1;
vis[G[q.front()][i]] = 1;
q.push(G[q.front()][i]);
}
}
q.pop();
}
return;
}

void build()
{
for(int i = 1; i <= n; i++)
{
for(unsigned int j = 0; j < G[i].size(); j++)
{
if(deep[i] < deep[G[i][j]])
{
F[i].pb(G[i][j]);
}
}
}
return;
}

bool check(int val, int root)//求mex()
{
for(unsigned int i = 0; i < F[root].size(); i++)
{
if(val == SG[F[root][i]]) return true;
}
return false;
}

void dfs(int root)//递归求解SG函数
{
if(F[root].empty())
{
SG[root] = 0;
return;
}
if(vis[root]) return;
for(unsigned int i = 0; i < F[root].size(); i++)
{
dfs(F[root][i]);
}
int cnt = 0;
while(check(cnt, root))
{
cnt++;
}
SG[root] = cnt;
vis[root] = 1;
return;
}

int main()
{
scanf("%d %d", &n, &m);
int tx,ty;
for(int i = 1; i <= m; i++)
{
scanf("%d %d", &tx, &ty);
G[tx].pb(ty);
G[ty].pb(tx);
}
bfs();
build();
clr(vis);
dfs(1);
if(SG[1] != 0)
{
printf("Vladimir\n");
}
else
{
printf("Nikolay\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息