题目描述:给出一个n乘m的网格图,一共有3种格子:空地,陷阱和墙。你只能在空地上放守卫,而且放下的守卫不能存在有两个守卫在同一行或同一列,而且没有墙隔着的情况。(1≤n,m≤200)
100%做法:
这题没有部分分,所以只能直接将100分的做法:这题一开始感觉像是玄学dp,但搞不出来,就往网络流的放向上去想了:考虑这样一个图:
5 6
0 2 1 0 1 0
1 1 1 0 1 1
0 1 0 1 1 1
1 0 2 1 0 0
0 1 0 1 0 1
其中白色代表空地,褐色五边形代表墙,红色爆炸型代表陷阱。
我们可以根据原图建出网络流的图,考虑将行和列分开考虑,根据这个图可以发现,第1行中,1-1列只能有一个守卫,2-6列只能有一个守卫,第2行1-6列只能有一个守卫......以此类推,列也类似,而我们考虑在第(i,j)个点放守卫,那么就要同时满足行和列的限制。所以可以建两排点,第一排点代表行的限制,第二排点表示列的限制,源点给每个行限制1的流量,每个列的限制只有一条1流量的边连向汇点当一个空格为(i,j)时就将对应的行限制向对应的列限制连一条流量为1的边。上图样例如下图建图:
这样,跑一遍dinic是完全不虚的。最大流即能放多少个守卫,额第1层点连向第2层点的边如果流掉了就是选了2层点对应的行和列上的点。
推荐番:《fate stay night》
上代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
struct data
{int type,i;};
data a[200005];
int n,m,h,t,dis[200005],cur[200005],pp[200005],ope[200005],v[200005],to[200005],next[200005],www;
int S,T,p,x,y,val,ans,last,bian,mapp[205][205],hang[205][205],lie[205][205],pand[200005];
bool bfs(int k)
{
for (int i=1;i<=bian;i++)dis[i]=0;dis[S]=0;dis[T]=0;
for (int i=1;i<=bian;i++)cur[i]=pp[i];cur[S]=pp[S];cur[T]=pp[T];
ope[1]=k;h=1;t=1;dis[k]=1;
while (h<=t)
{
int pu=pp[ope[h]];
while (pu>-1)
{
if (v[pu]>0 && dis[to[pu]]==0)
{
t++;ope[t]=to[pu];dis[to[pu]]=dis[ope[h]]+1;
if (to[pu]==T){return true;}
}
pu=next[pu];
}
h++;
}
return false;
}
int dfs(int a,int num)
{
if (a==T)return num;
int pu=cur[a],ff=0,fff=0;
while (pu>-1)
{
if (num==0)return fff;
if (v[pu]>0 && dis[to[pu]]==dis[a]+1)
{
ff=dfs(to[pu],min(num,v[pu]));
fff+=ff;
num-=ff;
v[pu]-=ff;v[pu^1]+=ff;
}
pu=next[pu];cur[a]=pu;
}
return fff;
}
int main()
{
cin>>n>>m;p=-1;S=100000;T=100001;pp[S]=-1;pp[T]=-1;
for (int i=1;i<=n*m*2;i++)pp[i]=-1;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
scanf("%d",&mapp[i][j]);
}
for (int i=1;i<=n;i++)
{
last=1;
for (int j=1;j<=m;j++)
{
{
if (mapp[i][j]==2 && last<j)
{
bian++;
for (int k=last;k<j;k++)
{
hang[i][k]=bian;
}
last=j+1;
a[bian].type=1;a[bian].i=i;
}
if (mapp[i][j]==2)last=j+1;
}
}
if (last<=m)
{
bian++;
for (int j=last;j<=m;j++)hang[i][j]=bian;
a[bian].type=1;a[bian].i=i;
}
}
www=bian;
for (int i=1;i<=m;i++)
{
last=1;
for (int j=1;j<=n;j++)
{
{
if (mapp[j][i]==2 && last<j)
{
bian++;
for (int k=last;k<j;k++)
{
lie[k][i]=bian;
}
last=j+1;
a[bian].type=2;a[bian].i=i;
}
if (mapp[j][i]==2)last=j+1;
}
}
if (last<=n)
{
bian++;
for (int j=last;j<=n;j++)lie[j][i]=bian;
a[bian].type=0;a[bian].i=i;
}
}
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
{
if (mapp[i][j]==0)
{
x=hang[i][j];y=lie[i][j];
p++;to[p]=y;v[p]=1;next[p]=pp[x];pp[x]=p;
p++;to[p]=x;v[p]=0;next[p]=pp[y];pp[y]=p;
if (pand[x]==0)
{
pand[x]=1;
p++;to[p]=x;v[p]=1;next[p]=pp[S];pp[S]=p;
p++;to[p]=S;v[p]=0;next[p]=pp[x];pp[x]=p;
}
if (pand[y]==0)
{
pand[y]=1;
p++;to[p]=T;v[p]=1;next[p]=pp[y];pp[y]=p;
p++;to[p]=y;v[p]=0;next[p]=pp[T];pp[T]=p;
}
}
}
}
while (bfs(S)){ans+=dfs(S,1000000007);}
printf("%d\n",ans);
for (int i=1;i<=www;i++)
{
int pu=pp[i];
while (pu>-1)
{
if (v[pu]==0 && to[pu]!=S){printf("%d %d\n",a[i].i,a[to[pu]].i);}
pu=next[pu];
}
}
}