• [JSOI2009]游戏


    题目

    给个有障碍物的(n imes m)棋盘,两个人轮流移动棋子,一个格子只能经过一次,不能移动的人算输;先手可以任选一个起点然后移动一格,问先手有多少个必胜的选法

    思路

    一人一步的东西,不是博弈论就是二分图了吧。。。毕竟上次被兔兔和蛋蛋坑了还是要记住的

    按照套路,对棋盘黑白染色;对二分图求最大匹配,如果该二分图不存在完备匹配,那么先手必输,否则所有可能成为非匹配点的点都可以作为起点

    因为从非匹配点出发,先手选择走匹配边走到一个匹配点,之后的路径就是走最大匹配的路径了,因为是偶数个点,所以最后一定是后手无路可走,可以证明一定后手无法走出这个最大匹配的路径,否则就会形成一条増广路

    跑个网络流,用个DFS跑出所有非匹配点就行了,这个DFS类似求増广路的过程,具体见代码

    Code

    #include<bits/stdc++.h>
    #define N 20005
    #define Min(x,y) ((x)<(y)?(x):(y))
    #define Max(x,y) ((x)>(y)?(x):(y))
    using namespace std;
    const int inf = 10000000;
    int n,m,s,t;
    int a[105][105];
    int dox[4]={0,1,0,-1},doy[4]={1,0,-1,0};
    int dep[N],pp[N],up[N];
    char c[105];
    bool vis[N],lef[N];
    struct Edge
    {
    	int next,to,flow;
    }edge[N<<3];int head[N],cur[N],cnt=1;
    void add_edge(int from,int to,int flow)
    {
    	edge[++cnt].next=head[from];
    	edge[cnt].to=to;
    	edge[cnt].flow=flow;
    	head[from]=cnt;
    }
    void add(int from,int to,int flow)
    {
    	add_edge(from,to,flow);
    	add_edge(to,from,0);
    }
    int id(int x,int y) {return (x-1)*m+y;}
    bool bfs()
    {
    	memset(dep,0,sizeof(dep));
    	queue<int> q;
    	dep[s]=1; q.push(s);
    	while(!q.empty())
    	{
    		int u=q.front(); q.pop();
    		for(int i=head[u];i;i=edge[i].next)
    		{
    			int v=edge[i].to;
    			if(dep[v]||edge[i].flow<=0) continue;
    			dep[v]=dep[u]+1;
    			q.push(v);
    			if(v==t) return true;
    		}
    	}
    	return false;
    }
    int dfs(int rt,int rest)
    {
    	if(rt==t) return rest;
    	int used=0;
    	for(int i=head[rt];i&&used<=rest;i=edge[i].next)
    	{
    		int v=edge[i].to;
    		if(dep[v]==dep[rt]+1&&edge[i].flow)
    		{
    			int k=dfs(v,Min(rest-used,edge[i].flow));
    			if(!k) dep[v]=0;
    			edge[i].flow-=k;
    			edge[i^1].flow+=k;
    			used+=k;
    		}
    	}
    	return used;
    }
    int dinic()
    {
    	int ret=0;
    	while(bfs()) ret+=dfs(s,inf);
    	return ret;
    }
    void DFS(int rt,int lim)
    {
    	if(vis[rt]) return;
        vis[rt]=1;
        if(lef[rt]==lim) pp[rt]=1;
        for(int i=head[rt];i;i=edge[i].next) if(edge[i].flow==lim) DFS(edge[i].to,lim);
        
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	s=0;t=n*m+1;
    	for(int i=1;i<=n;++i)
    	{
    		scanf("%s",c);
    		for(int j=1;j<=m;++j) a[i][j]=(c[j-1]=='.');
    	}
    	int sum=0;
    	for(int i=1;i<=n;++i)
    	{
    		for(int j=1;j<=m;++j)
    		{
    			if(a[i][j])
    			{
    				++sum;
    				int fr=id(i,j);
    				if((i+j)%2) add(s,fr,1),lef[fr]=1;
    				else add(fr,t,1);
    				
    				if((i+j)%2) 
    				for(int k=0;k<4;++k)
    				{
    					int dx=dox[k]+i,dy=doy[k]+j;
    					if(dx<1||dy<1||dx>n||dy>m||!a[dx][dy]) continue;
    					int to=id(dx,dy);
    					add(fr,to,1);
    				}
    			}
    		}
    	}
    	int ans=dinic()*2;
    	if(sum==ans) { puts("LOSE"); return 0; }
    	puts("WIN");
    	DFS(s,1);
    	memset(vis,0,sizeof(vis));
    	DFS(t,0);
    	
    	for(int i=1;i<=n;++i)
    	  for(int j=1;j<=m;++j)
    		if(pp[id(i,j)]&&a[i][j]) printf("%d %d
    ",i,j);
    	return 0;
    }
    
  • 相关阅读:
    ASP.NET(C#)——唯一订单号
    Oracle——备份与还原
    ASP.NET(C#)——日期函数
    数据安全——数据安全标准
    文件内容的追加
    文件的读取
    创建文件,写文件
    遍历文件改进
    遍历文件夹
    递归方法求前n项和
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11789457.html
Copyright © 2020-2023  润新知