您的位置:首页 > Web前端 > JavaScript

BZOJ 1443 [JSOI2009]游戏Game | UVALive 5882 Racing Car Trail

2016-06-22 23:21 387 查看
题目:

http://www.lydsy.com/JudgeOnline/problem.php?id=1443

https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3893

题意:

有一个N×M的棋盘,其中一些格子有障碍,现在有两个人玩游戏,棋盘上有一个棋子,每个人轮流移动棋子,但是不能移动到障碍,也不能移动到之前走过的格子,不能移动的人输,对于每个可能的起点,计算先手是否存在必胜策略。

N,M≤100

题解:

对棋盘中可以互相到达的点之间连边,可以得到一个二分图。

对于二分图,有一个性质是最大匹配数等于最小顶点覆盖数。

最大匹配数是指,选出尽量多的边,使得任意选出的两条边不共用顶点。

最小顶点覆盖是指,选出尽量少的点,使得图中任意一个点要么被选,要么与一个被选的点相连。

证明方法也很简单

最大匹配的每条边各选出一个点,即可覆盖最大匹配中的每个点,如果存在一个点没被覆盖,那么这个点与相连的点能产生一个更大的匹配,因此,每个点至少和一个在匹配里的点相连,|最小点覆盖|≤|最大匹配|。

最大匹配的顶点互不相同,每个匹配里至少要选出一个点才能覆盖所有的匹配,因此|最小点覆盖|≥|最大匹配|。

综上所述,|最小点覆盖|=|最大匹配|。

通过证明过程也可以看出,如果先手从一个一定在最大匹配里的点出发,则一定可以走到一个可能不在最大匹配里的点,反之则可能走不到。

通过这一点就可以确定先手必胜的条件是起点一定在最大匹配中,否则只需要起点是可能不在最大匹配里的点,先手若能移动,后手总能移动到一个在最大匹配里的点。

考虑交替增广路的过程,先求出一个最大匹配,如果一个不在最大匹配里的点可以通过交替增广路变成一个最大匹配里的点,则那个被剥夺匹配的点也不一定在最大匹配中,这个过程可以利用最大流来轻松实现。

考虑将点分为
X集
Y集
源点
X集
的点连边,
X集
Y集
的点连边,
Y集
汇点
连边,边的容量均为1。显然,如果最大匹配是完美匹配,则所有点都是先手必胜的。实际上,我们只需要找出后手必胜的点是哪些即可。

考虑残量网络,
源点
能直接到达的
X集
的点
,是不在最大匹配里的点,这些点通过走
Y集
的点回到
X集
的点
,路径构成一条交替增广路,则回到的
X集
的点也可以不在最大匹配里;
汇点
能反向到达的
Y集
的点
,是不在最大匹配里的点,这些点通过反向走
X集
的点回到
Y集
的点
,反向路径构成一条交替增广路,则回到的
Y集
的点也可以不在最大匹配里。

所以计算完最大匹配后,dfs一下就好了,时间复杂度O((nm)1.5)。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
const int maxs = 101, maxn = maxs * maxs, maxm = maxn * 3;
int n, m, N, M, S, T, lnk[maxn], level[maxn], que[maxn], L, R;
char str[maxs][maxs];
struct Edge
{
int nxt, v, f;
} e[maxm << 1];
inline int tidx(int x, int y)
{
return x * m + y;
}
inline void ridx(int o, int &x, int &y)
{
x = o / m;
y = o % m;
}
void addEdge(int u, int v, int c)
{
e[M] = (Edge){lnk[u], v, c};
lnk[u] = M++;
e[M] = (Edge){lnk[v], u, 0};
lnk[v] = M++;
}
bool bfs()
{
L = R = 0;
memset(level, 0, N * sizeof(int));
level[S] = 1;
que[R++] = S;
while(L < R)
{
int u = que[L++];
for(int it = lnk[u]; it != -1; it = e[it].nxt)
if(e[it].f && !level[e[it].v])
{
level[e[it].v] = level[u] + 1;
que[R++] = e[it].v;
}
}
return level[T];
}
int dfs(int u, int lim)
{
if(u == T || !lim)
return lim;
int ret = 0;
for(int it = lnk[u], tmp; it != -1; it = e[it].nxt)
if(e[it].f && level[e[it].v] == level[u] + 1
&& (tmp = dfs(e[it].v, std::min(e[it].f, lim - ret))))
{
e[it].f -= tmp;
e[it ^ 1].f += tmp;
ret += tmp;
}
if(!ret)
level[u] = 0;
return ret;
}
bool vis[maxn];
void check(int u, int o)
{
vis[u] = 1;
if(u < n * m)
{
int x, y;
ridx(u, x, y);
if(((x & 1) ^ (y & 1)) == o)
str[x][y] = '*';
}
for(int it = lnk[u]; it != -1; it = e[it].nxt)
if(e[it ^ o].f && !vis[e[it].v])
check(e[it].v, o);
}
int main()
{
int cL = 0, cR = 0, flow = 0;
scanf("%d%d", &n, &m);
N = n * m + 2;
M = 0;
S = N - 2;
T = N - 1;
memset(lnk, -1, N * sizeof(int));
for(int i = 0; i < n; ++i)
{
scanf("%s", str[i]);
for(int j = 0; j < m; ++j)
{
if(str[i][j] == '#')
continue;
if((i & 1) == (j & 1))
{
++cL;
addEdge(S, tidx(i, j), 1);
if(i > 0 && str[i - 1][j] != '#')
addEdge(tidx(i, j), tidx(i - 1, j), 1);
if(j > 0 && str[i][j - 1] != '#')
addEdge(tidx(i, j), tidx(i, j - 1), 1);
}
else
{
++cR;
addEdge(tidx(i, j), T, 1);
if(i > 0 && str[i - 1][j] != '#')
addEdge(tidx(i - 1, j), tidx(i, j), 1);
if(j > 0 && str[i][j - 1] != '#')
addEdge(tidx(i, j - 1), tidx(i, j), 1);
}
}
}
while(bfs())
for(int tmp; tmp = dfs(S, maxn); flow += tmp);
if(flow == cL && flow == cR)
{
puts("LOSE");
return 0;
}
check(S, 0);
memset(vis, 0, N * sizeof(bool));
check(T, 1);
puts("WIN");
for(int i = 0; i < n; ++i)
for(int j = 0; j < m; ++j)
if(str[i][j] == '*')
printf("%d %d\n", i + 1, j + 1);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  二分图 网络流