初见安~这里是传送门:洛谷P1971 [NOI2011]兔兔与蛋蛋游戏
这种题啊……就只能多悟,才能看到了就知道怎么做【bushi。
看题目的操作:每次交替移动黑白棋子。然后就可以想到二分图了【??!什么鬼。
空白格子从初始到终点走的路径上的格子都是与之交换过的,并且黑白相间,该路径与以前走过的路径不可能有交点。转化到二分图则是:把黑格子的点放一边,白格子的点放一边,每次操作都是从一边移动到另一边。
上文是用二分图模拟这个过程,现在我们再来看如何判定。因为都采用最优策略,所以如果从一个点出发后最多可以走奇数条边,那么先手必胜,反之后手必胜。这个很好理解。那么换到这个题上,如果兔兔本来在的点有先手必胜策略,但操作过后让蛋蛋有了先手必胜策略,那么这一步就是错的。对于最大边数的判定我们可以用到二分图匹配。即我们先求出一个最大匹配,假设当前点是u,在最大匹配中,如果把u删掉后还能找到新的增广路来代替,那么u先手必败。很好理解——u是匹配点,那么路径应该是匹配边—非匹配边—匹配边.. 删去后:非匹配边—匹配边—...匹配边,最后一定是匹配边结尾【否则就有新的增广路了,同理一条非匹配边后也一定能找到一条匹配边走下一步】,也就是说u作为匹配点时是非匹配边结尾,那么边数就是偶数的,先手必败。
综上——我们先求出该二分图的最大匹配,再按照实际的下棋顺序模拟过程,求出每一步时的点是否有必胜策略,再单独看兔兔走的点即可【因为要统计有多少步】
上代码——
#include
#include
#include
#include
#include
#include
#define maxn 50
#define maxk 2005
using namespace std;
typedef long long ll;
int read() {
int x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
char a[maxn][maxn];
int n, m, mp[maxn][maxn], s, t, Q;
struct edge {int to, nxt;} e[9000000];
int head[maxn * maxn], k = 0;
void add(int u, int v) {e[k] = {v, head[u]}; head[u] = k++;}
int dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int id[maxn][maxn], mat[maxn * maxn], ans[maxk], tot = 0;
bool vis[maxn * maxn], ban[maxn * maxn], win[maxk];
bool in(int x, int y) {return 0 < x && x <= n && 0 < y && y <= m;}
bool dfs(int u) {//匈牙利求增广路
if(ban[u]) return false;
for(int i = head[u], v; ~i; i = e[i].nxt) {
v = e[i].to; if(vis[v] || ban[v]) continue;
vis[v] = true;
if(!mat[v] || dfs(mat[v])) {mat[u] = v, mat[v] = u; return true;}
}
return false;
}
signed main() {
memset(head, -1, sizeof head);
n = read(), m = read();
for(int i = 1; i <= n; i++) scanf("%s", a[i] + 1);
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) {
mp[i][j] = (a[i][j] == 'O'? 1 : 2);
if(a[i][j] == '.') s = i, t = j;
id[i][j] = m * (i - 1) + j;
}
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) //连边
for(int p = 0; p < 4; p++) {
register int tx = i + dir[p][0], ty = j + dir[p][1];
if(in(tx, ty) && mp[i][j] != mp[tx][ty]) add(id[i][j], id[tx][ty]), add(id[tx][ty], id[i][j]);
}
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) if(mp[i][j] == 2)//求增广路
memset(vis, 0, sizeof vis), dfs(id[i][j]);
Q = read();
for(int i = 1; i <= Q * 2; i++) {//模拟过程
register int u = id[s][t], v; ban[u] = true;//走过的点不能考虑了
if(mat[u]) {//如果是匹配点
memset(vis, 0, sizeof vis); v = mat[u];
mat[u] = mat[v] = 0;//删去这条匹配边
win[i] = 1 ^ dfs(v);//这里可以注意一下
}
s = read(), t = read();
}
for(int i = 1; i <= Q; i++) if(win[i * 2 - 1] && win[i * 2]) ans[++tot] = i;
printf("%d\n", tot);
for(int i = 1; i <= tot; i++) printf("%d\n", ans[i]);
return 0;
}
迎评:)
——End——