二分图博弈
考虑这样一类博弈问题
- 博弈状态可分为两类 即状态空间可分为两个集合 对应二分图X集和Y集
- 任意合法的决策使状态从一类跳转到另一类 可以用二分图描述
- 不可以转移至已访问的状态
- 无法转移者判负。
问题转化从二分图指定起点开始轮流沿着边移动 不可重复访问点 无法移动判负。
不妨设起点 s∈X 考虑该二分图的某个最大匹配
- 若
s
不属于某个最大匹配 则先手所转移到的点
y∈Y 一定属于最大匹配 后手沿着最大匹配的边走即可 后手不可能无路可走 因为这样对应着找到一条增广链 这与最大匹配矛盾 后手必胜 - 若任意最大匹配都包含
s
则先手沿着最大匹配的边走即可 因为
s 必属于最大匹配 所以不存在长度为偶数的链 先手必胜
所以
若起点 s <script type="math/tex" id="MathJax-Element-50">s</script>是二分图最大匹配必配点 那么先手必胜 否则后手必胜
1443
很裸的二分图博弈
我们用最大流求出任意最大匹配
再用找弱增广链(长为偶数的链)的方法求出最大匹配必配点即可
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline void read(char *s){
char c=nc(); int len=0;
for (;!(c=='.' || c=='#');c=nc());
for (;c=='.' || c=='#';s[++len]=c,c=nc());
}
#define U G[p].u
#define V G[p].v
const int N=10005;
namespace DINIC{
struct edge{
int u,v,f,next;
}G[N<<4];
int head[N],inum=1;
inline void add(int u,int v,int f,int p){
G[p].u=u; G[p].v=v; G[p].f=f; G[p].next=head[u]; head[u]=p;
}
inline void link(int u,int v,int f){
add(u,v,f,++inum); add(v,u,0,++inum);
}
int S,T;
int Q[N],l,r;
int dis[N];
inline bool bfs(){
for (int i=1;i<=T;i++) dis[i]=-1;
l=r=-1; Q[++r]=S; dis[S]=0;
while (l<r){
int u=Q[++l];
for (int p=head[u];p;p=G[p].next)
if (G[p].f && dis[V]==-1){
dis[V]=dis[u]+1; Q[++r]=V;
if (V==T) return 1;
}
}
return 0;
}
int cur[N];
inline int dfs(int u,int flow){
if (u==T) return flow;
int used=0;
for (int p=cur[u];p;p=G[p].next){
cur[u]=p;
if (G[p].f && dis[V]==dis[u]+1){
int d=dfs(V,min(flow-used,G[p].f));
G[p].f-=d; G[p^1].f+=d;
used+=d; if (used==flow) break;
}
}
if (used==0) dis[u]=-1;
return used;
}
inline void Dinic(){
while (bfs())
memcpy(cur,head,sizeof(head)),dfs(S,1<<30);
}
}
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
int n,m;
#define P(x,y) (((x)-1)*m+y)
char Map[105][105];
int left[N],right[N];
inline void Build(){
using namespace DINIC;
int sx,sy; S=n*m+1; T=n*m+2;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if ((i+j)&1){
if (Map[i][j]=='#') continue;
for (int k=0;k<4;k++){
sx=i+dx[k],sy=j+dy[k];
if (sx<1 || sy<1 || sx>n || sy>m || Map[sx][sy]=='#') continue;
link(P(i,j),P(sx,sy),1);
}
link(S,P(i,j),1);
}else
link(P(i,j),T,1);
Dinic();
for (int p=2;p<=inum;p+=2)
if (U!=S && V!=T && !G[p].f){
left[V]=U,right[U]=V;
}
}
struct edge{
int u,v,next;
}G[N<<2];
int head[N],inum;
inline void add(int u,int v,int p){
G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}
int vst[N];
inline void dfs(int u){
if (vst[u]) return;
vst[u]=1;
for (int p=head[u];p;p=G[p].next)
dfs(V);
}
typedef pair<int,int> abcd;
int pnt;
abcd ans[N];
int main(){
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(n); read(m);
for (int i=1;i<=n;i++) read(Map[i]);
Build();int sx,sy;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if ((i+j)&1){
if (Map[i][j]=='#') continue;
for (int k=0;k<4;k++){
sx=i+dx[k],sy=j+dy[k];
if (sx<1 || sy<1 || sx>n || sy>m || Map[sx][sy]=='#') continue;
if (right[P(i,j)]==P(sx,sy))
add(P(sx,sy),P(i,j),++inum);
else
add(P(i,j),P(sx,sy),++inum);
}
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (((i+j)&1) && !right[P(i,j)])
dfs(P(i,j));
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (((i+j)&1) && vst[P(i,j)] && Map[i][j]!='#')
ans[++pnt]=abcd(i,j);
cl(head); inum=0; cl(vst);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (~(i+j)&1){
if (Map[i][j]=='#') continue;
for (int k=0;k<4;k++){
sx=i+dx[k],sy=j+dy[k];
if (sx<1 || sy<1 || sx>n || sy>m || Map[sx][sy]=='#') continue;
if (left[P(i,j)]==P(sx,sy))
add(P(sx,sy),P(i,j),++inum);
else
add(P(i,j),P(sx,sy),++inum);
}
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if ((~(i+j)&1) && !left[P(i,j)])
dfs(P(i,j));
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if ((~(i+j)&1) && vst[P(i,j)] && Map[i][j]!='#')
ans[++pnt]=abcd(i,j);
sort(ans+1,ans+pnt+1);
if (pnt==0)
printf("LOSE\n");
else{
printf("WIN\n");
for (int i=1;i<=pnt;i++)
printf("%d %d\n",ans[i].first,ans[i].second);
}
return 0;
}
2437
首先空格的移动不可能出现环
因为先手是白方 我们把空格当作黑格
那么空格的移动 对应一条黑白相间的简单路径
我们只要建出二分图 判断这一路径是否唯一即可
因为这道题多次询问 需要删点操作 所以不能直接处理出必配点
只要每次删点后再增广一遍 看看最大匹配是否减小就能知道是否是先手必胜
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline void read(char *s){
char c=nc(); int len=0;
for (;!(c=='O' || c=='X' || c=='.');c=nc());
for (;c=='O' || c=='X' || c=='.';s[++len]=c,c=nc());
}
#define U G[p].u
#define V G[p].v
const int N=1605;
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
int n,m;
#define P(x,y) (((x)-1)*m+y)
char Map[105][105];
int left[N],right[N];
struct edge{
int u,v,next;
}G[N<<2];
int head[N],inum;
inline void add(int u,int v,int p){
G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}
inline void link(int u,int v){
add(u,v,++inum),add(v,u,++inum);
}
int vst[N],del[N],boy[N];
inline bool dfs(int u){
for (int p=head[u];p;p=G[p].next)
if (!vst[V] && !del[V]){
vst[V]=1;
if (!boy[V] || dfs(boy[V]))
return boy[V]=u,boy[u]=V,1;
}
return 0;
}
inline void Build(){
int sx,sy;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if ((i+j)&1)
for (int k=0;k<4;k++){
sx=i+dx[k],sy=j+dy[k];
if (sx<1 || sy<1 || sx>n || sy>m) continue;
if (Map[sx][sy]!=Map[i][j])
link(P(i,j),P(sx,sy));
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (!boy[P(i,j)]){
cl(vst);
dfs(P(i,j));
}
}
inline bool Jud(int x,int y){
int u=P(x,y);
del[u]=1;
if (boy[u]){
int tmp=boy[u];
boy[u]=boy[tmp]=0;
cl(vst);
return !dfs(tmp);
}else
return 0;
}
int pnt,ans[N];
int main(){
int Q,nx,ny,_x,_y;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(n); read(m);
for (int i=1;i<=n;i++) read(Map[i]);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (Map[i][j]=='.') Map[i][j]='X',nx=i,ny=j;
Build();
read(Q);
for (int i=1;i<=Q;i++){
read(_x); read(_y);
int t1=Jud(nx,ny),t2=Jud(_x,_y);
if (t1 && t2)
ans[++pnt]=i;
read(nx); read(ny);
}
printf("%d\n",pnt);
for (int i=1;i<=pnt;i++)
printf("%d\n",ans[i]);
return 0;
}