zoukankan      html  css  js  c++  java
  • BZOJ.2437.[NOI2011]兔兔与蛋蛋游戏(二分图博弈 匈牙利)

    题目链接

    首先空格的移动等价于棋子在黑白格交替移动(设起点移向白格就是黑色),且不会走到到起点距离为奇数的黑格、到起点距离为偶数的白格(删掉就行了),且不会重复走一个格子。
    (然后策略就同上题了,只不过第一步是走棋子)
    还是考虑二分图最大匹配。如果起点不一定在最大匹配上,先手走到最大匹配点,后手沿最大匹配边走,先手要么无法走要么回到刚刚的情况,即先手必败(最大匹配是一条奇数长路径)。
    反之,如果起点一定在最大匹配上,先手必胜。
    判断一个点是否一定在最大匹配上可以先求一遍,再对非匹配点DFS。但是本题有多次移动,相当于删掉之前的点再求最大匹配。
    如果删掉的点一定在最大匹配上,那么移动前该人必胜。
    判断删的点是否一定在最大匹配中:首先当前在最大匹配中,其次删掉这个点x后,没有新的增广路(match[x]找不到新匹配)。

    删点后记得清空连着的点的link啊。

    //1212kb	36ms
    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    //#define gc() getchar()
    #define MAXIN 300000
    #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    const int N=1605,M=N<<2;
    
    int n,m,mp[50][50],id[50][50],Enum,H[N],nxt[M],to[M],lk[N],Time,vis[N];
    bool ban[N],ans[2005];
    char IN[MAXIN],*SS=IN,*TT=IN;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    inline void AE(int u,int v)
    {
    	to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
    	to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
    }
    bool OK(int x)
    {
    	vis[x]=Time;
    	for(int i=H[x],v; i; i=nxt[i])
    		if(vis[v=to[i]]!=Time && !ban[v])
    		{
    			vis[v]=Time;
    			if(!lk[v]||OK(lk[v])) return lk[v]=x,lk[x]=v,1;
    		}
    	return 0;
    }
    
    int main()
    {
    	n=read(),m=read(); int x=0,y=0;
    	for(int i=1; i<=n; ++i)
    	{
    		register char c=gc();
    		for(; c!='X'&&c!='O'&&c!='.'; c=gc());
    		for(int j=1; j<=m; ++j,c=gc())
    			if(c=='X') mp[i][j]=1;//black
    			else if(c=='O') mp[i][j]=0;//white
    			else x=i, y=j, mp[i][j]=1;
    	}
    	int f=(x+y)&1,tot=0;
    	for(int i=1; i<=n; ++i)
    		for(int j=1; j<=m; ++j)
    			if(mp[i][j]^f^((i+j)&1))//f^is_black^(i+j)&1
    				id[i][j]=++tot;
    	for(int i=1; i<=n; ++i)
    		for(int j=1; j<=m; ++j)
    		{
    			if(!id[i][j]) continue;//!
    			if(id[i+1][j]) AE(id[i][j],id[i+1][j]);
    			if(id[i][j+1]) AE(id[i][j],id[i][j+1]);
    		}
    	for(int i=1; i<=tot; ++i) if(!lk[i]/*!*/) ++Time, OK(i);
    	int K=read()<<1;
    	for(int i=1,p; i<=K; ++i)
    	{
    		ban[p=id[x][y]]=1;
    		if(lk[p]) ++Time, lk[lk[p]]=0/*清空!*/, ans[i]=!OK(lk[p]);//(x,y)是否是必胜态 
    		x=read(), y=read();
    	}
    	int res=0;
    	for(int i=1; i<=K; i+=2) if(ans[i]&&ans[i+1]) ++res;
    	printf("%d
    ",res);
    	for(int i=1; i<=K; i+=2) if(ans[i]&&ans[i+1]) printf("%d
    ",i+1>>1);
    
    	return 0;
    }
    
  • 相关阅读:
    python-字典
    C#公历转农历算法
    GridView控件显示图片
    SQLite DBHelp
    面向服务体系结构:适用于敏捷的系统
    针对 .NET 框架的安全编码指南
    Microsoft .NET Pet Shop 4
    C#.NET数据库访问类DBHelper
    Emgu CV 高斯建模
    .NET代码编写规范 整理
  • 原文地址:https://www.cnblogs.com/SovietPower/p/9592769.html
Copyright © 2011-2022 走看看