• LOJ6033「雅礼集训 2017 Day2」棋盘游戏 (博弈论,二分图,匈牙利算法)


    什么神仙思路啊……

    看到棋盘就去想二分图。(smg啊)(其实是校内模拟赛有基本一样的题,只不过直接给了个二分图)

    看到二分图就去想最大匹配。(我怎么想偶环的性质去了)

    (以下内容摘自这里

    这个二分图的某种最大匹配方案中,从非匹配点出发先手必败:先手只能走到匹配点(否则不是最大匹配),后手只需要一直走匹配边即可,先手操作时不可能走到非匹配点(否则存在增广路,与最大匹配矛盾),所以先手必败。

    容易发现,当且仅当出发点一定在最大匹配中,先手才会胜利。

    (注:这里我觉得有点问题,虽然我大概能感受到究竟为什么是对的,但我说不出,所以咕了)

    所以,一个起点使先手必胜当且仅当它一定在最大匹配中。

    判断的话,先求出任意一个最大匹配。然后对有匹配的点,如果与他有匹配的点能找到另一个匹配,那么它实际上不合法,否则合法。

    用匈牙利算法即可做到 (O((nm)^2))。实际上常数小,跑不满,跑得飞快。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=100010,mod=998244353,d[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
    #define lson o<<1,l,mid
    #define rson o<<1|1,mid+1,r
    #define FOR(i,a,b) for(int i=(a);i<=(b);i++)
    #define ROF(i,a,b) for(int i=(a);i>=(b);i--)
    #define MEM(x,v) memset(x,v,sizeof(x))
    inline int read(){
    	int x=0,f=0;char ch=getchar();
    	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    	return f?-x:x;
    }
    int n,m,el,cnt,head[maxn],to[maxn],nxt[maxn],dis[maxn],with[maxn],x[maxn],y[maxn],id[111][111],tot;
    char mp[111][111];
    bool vis[maxn],ans[maxn];
    inline void add(int u,int v){
    	to[++el]=v;nxt[el]=head[u];head[u]=el;
    }
    void dfs(int u){
    	for(int i=head[u];i;i=nxt[i]){
    		int v=to[i];
    		if(~dis[v]) continue;
    		dis[v]=dis[u]^1;
    		dfs(v);
    	}
    }
    bool dfs2(int u){
    	if(vis[u]) return false;
    	vis[u]=true;
    	for(int i=head[u];i;i=nxt[i]){
    		int v=to[i];
    		if(!with[v] || dfs2(with[v])){
    			with[with[u]]=0;
    			with[u]=v;with[v]=u;
    			return true;
    		}
    	}
    	return false;
    }
    int main(){
    	n=read();m=read();
    	FOR(i,1,n) scanf("%s",mp[i]+1);
    	FOR(i,1,n) FOR(j,1,m) if(mp[i][j]=='.') id[i][j]=++cnt,x[cnt]=i,y[cnt]=j;
    	FOR(i,1,n) FOR(j,1,m) if(mp[i][j]=='.'){
    		FOR(k,0,3){
    			int ti=i+d[k][0],tj=j+d[k][1];
    			if(ti<1 || ti>n || tj<1 || tj>m || mp[ti][tj]=='#') continue;
    			add(id[i][j],id[ti][tj]);
    		}
    	}
    	MEM(dis,-1);
    	FOR(i,1,cnt) if(dis[i]==-1) dis[i]=0,dfs(i);
    	FOR(i,1,cnt) if(dis[i]){
    		MEM(vis,0);
    		dfs2(i);
    	}
    	FOR(i,1,cnt) if(with[i]){
    		MEM(vis,0);
    		ans[i]=!dfs2(with[i]);
    	}
    	FOR(i,1,cnt) if(!ans[i]) tot++;
    	printf("%d
    ",tot);
    	FOR(i,1,cnt) if(!ans[i]) printf("%d %d
    ",x[i],y[i]);
    }
    
  • 相关阅读:
    WebService的学习
    什么是事物
    数组和链表的区别
    JDK6和JDK7中的substring()方法
    为什么存储密码字符数组比字符串更合适?
    java中Queue简介
    java中队列Queue的使用
    HashMap、Hashtable、TreeMap的区别
    笔试算法题(36):寻找一棵二叉树中最远节点的距离 & 根据二叉树的前序和后序遍历重建二叉树
    笔试算法题(35):最长递增子序列 & 判定一个字符串是否可由另一个字符串旋转得到
  • 原文地址:https://www.cnblogs.com/1000Suns/p/11806024.html
Copyright © 2020-2023  润新知