【NOI2011】兔兔与蛋蛋的游戏【二分图博弈】

传送门

结论 不会有同一个棋子移动两次

反证法,对于第一个移动第二次的棋子

设两次移动之间(含)的移动的棋子为 A 1 , A 2 , A 3 , … … , A n A_1,A_2,A_3,……,A_n A1,A2,A3,An(指棋子本身而非位置)

因为最后移回来了,所以往上和往下、往左和往右次数相同

所以 n n n是偶数 然后 A 1 A_1 A1 A n A_n An颜色不同

但这个棋子开始被移到当前位置,最后被移走,所以 A 1 = A n A_1=A_n A1=An,矛盾

所以移动路径没有交

既然这样,我们就不用考虑棋盘具体的变化,而看成空格交替走两个颜色

然后和上一道题就一样了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#define MAXN 2005
#define MAXM 10005
using namespace std;
struct edge{int u,v;}e[MAXM];
int head[MAXN],nxt[MAXM],cnt;
void addnode(int u,int v)
{
     e[++cnt]=(edge){u,v};
     nxt[cnt]=head[u];
     head[u]=cnt;
}
int n,m;
#define id(x,y) (((x)-1)*m+(y))
char s[50][50];
const int dx[]={-1,1,0,0},dy[]={0,0,-1,1};
bool del[MAXN];
int link[MAXN],used[MAXN];
bool find(int u)
{
     for (int i=head[u];i;i=nxt[i])
	  if (!used[e[i].v]&&!del[e[i].v])
	  {
	       used[e[i].v]=1;
	       if (!del[link[e[i].v]]&&(!link[e[i].v]||find(link[e[i].v])))
	       {
		    link[u]=e[i].v,link[e[i].v]=u;
		    return true;
	       }
	  }
     return false;
}
int calc()
{
     memset(link,0,sizeof(link));
     int ans=0;
     for (int x=1;x<=n;x++)
	  for (int y=1;y<=m;y++)
	       if (s[x][y]!='O'&&!del[id(x,y)])
		    memset(used,0,sizeof(used)),ans+=find(id(x,y));
     return ans;
}
bool win[MAXN];
int main()
{
     scanf("%d%d",&n,&m);
     for (int i=1;i<=n;i++) scanf("%s",s[i]+1);
     int st;
     for (int x=1;x<=n;x++)
	  for (int y=1;y<=m;y++)
	       if (s[x][y]!='O')
	       {
		    for (int i=0;i<4;i++)
			 if (s[x+dx[i]][y+dy[i]]=='O')
			      addnode(id(x,y),id(x+dx[i],y+dy[i]));
		    if (s[x][y]=='.') st=id(x,y);
	       }
     int k,tot=0;
     scanf("%d",&k);
     int mmat=calc();
     for (int i=1;i<=2*k;i++)
     {
	  del[st]=1;
	  int t=calc();
	  if (mmat!=t) win[i]=1;
	  mmat=t;
	  int x,y;
	  scanf("%d%d",&x,&y);
	  st=id(x,y);
     }
     for (int i=1;i<=k;i++) if (win[2*i-1]&&win[2*i]) ++tot;
     printf("%d\n",tot);
     for (int i=1;i<=k;i++) if (win[2*i-1]&&win[2*i]) printf("%d\n",i); 
     return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值