zoukankan      html  css  js  c++  java
  • BZOJ1443 [JSOI2009]游戏Game 【博弈论 + 二分图匹配】

    题目链接

    BZOJ1443

    题解

    既然是网格图,便可以二分染色
    二分染色后发现,游戏路径是黑白交错的
    让人想到匹配时的增广路
    后手要赢【指移动的后手】,必须在一个与起点同色的地方终止

    容易想到完全匹配的图先手是必胜的,因为完全匹配的图要么走到对面终止,要么从对面找一条非匹配边走回来,而由于是完全匹配,总能继续走下去,所以先手总能走到一个不同色点
    于是乎对于一个匹配完的二分图,我们从一个未匹配的点出发,此时先手只能走未匹配边,而由于已经是匹配完毕,所以走到的点一定是已匹配的点,此时我们可以继续走到该点的匹配点,先手要么无法操作,要么继续走一条未匹配边
    换言之,我们走的总是交错边,而交错边上的匹配情况是可以互换的,所以从一个未匹配的点出发能走到的同侧的点都是后手必胜的

    也不知道为什么(O(n^3))怎么能跑(10^4)

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<map>
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define mp(a,b) make_pair<int,int>(a,b)
    #define cls(s) memset(s,0,sizeof(s))
    #define cp pair<int,int>
    #define LL long long int
    using namespace std;
    const int maxn = 10005,maxm = 1000005,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    	return out * flag;
    }
    int N,M,n,cnt;
    int h[maxn],ne;
    struct EDGE{int to,nxt;}ed[maxm];
    inline void build(int u,int v){
    	ed[++ne] = (EDGE){v,h[u]}; h[u] = ne;
    	ed[++ne] = (EDGE){u,h[v]}; h[v] = ne;
    }
    char s[105][105];
    int id[105][105],c[maxn],x[maxn],y[maxn];
    void dfs1(int u){
    	Redge(u) if (c[to = ed[k].to] == -1)
    		c[to] = c[u] ^ 1,dfs1(to);
    }
    int lk[maxn],vis[maxn],ok[maxn];
    bool find(int u){
    	Redge(u) if (!vis[to = ed[k].to]){
    		vis[to] = true;
    		if (!lk[to] || find(lk[to])){
    			lk[to] = u;
    			return true;
    		}
    	}
    	return false;
    }
    void dfs(int u){
    	if (ok[u]) return;
    	ok[u] = true;
    	Redge(u) if (lk[to = ed[k].to]) dfs(lk[to]);
    }
    inline bool cmp(const int& a,const int& b){
    	return x[a] == x[b] ? y[a] < y[b] : x[a] < x[b];
    }
    int main(){
    	N = read(); M = read();
    	REP(i,N){
    		scanf("%s",s[i] + 1);
    		REP(j,M) if (s[i][j] == '.') id[i][j] = ++n,x[n] = i,y[n] = j;
    	}
    	REP(i,N) REP(j,M){
    		if (!id[i][j]) continue;
    		if (id[i + 1][j]) build(id[i][j],id[i + 1][j]);
    		if (id[i][j + 1]) build(id[i][j],id[i][j + 1]);
    	}
    	REP(i,n) c[i] = -1;
    	REP(i,n) if (c[i] == -1) c[i] = 0,dfs1(i);
    	REP(i,n) if (c[i]) cls(vis),cnt += find(i);
    	if (cnt * 2 == n){puts("LOSE"); return 0;}
    	REP(i,n) if (!c[i]) lk[lk[i]] = i;
    	REP(i,n) if (!lk[i]) dfs(i);
    	puts("WIN");
    	REP(i,n) if (ok[i]) printf("%d %d
    ",x[i],y[i]);
    	return 0;
    }
    
    
  • 相关阅读:
    dotnetCore增加MiddleWare的Run,Use Map MapThen四个扩展方法
    人脸识别FaceNet+TensorFlow
    人体姿态估计(骨骼关节点检测)发展历程回顾
    基于人脸的用户识别方案及思路
    人脸检测学习笔记(数据集-DLIB人脸检测原理-DLIB&OpenCV人脸检测方法及对比)
    基于opencv+ffmpeg的镜头分割
    如何让两个线程交替打印整数1-100?你的答案呢?
    Ubuntu16.04+TensorFlow r1.12环境搭建指南
    在Eclipse中打jar包
    Linux常用快捷键
  • 原文地址:https://www.cnblogs.com/Mychael/p/9250861.html
Copyright © 2011-2022 走看看