您的位置:首页 > 其它

1412: [ZJOI2009]狼和羊的故事 最小割

2016-01-17 09:59 260 查看
高一的时候似乎做了……然后我现在发现不会做了。。

(高一代码抄多了QAQ)

题目要求的是将狼和羊分开,可以看做是分成两个点集,要使代价最小,那么也就是求一个最小割。

首先S->狼连边,权值为+∞。羊->T连边,权值为+∞,这些边都是不可割的。

然后我们把相邻的狼->羊连边,容量为1,代表在这个狼和羊之间建立篱笆的代价为1。

然后关于空地的问题。我们把狼->空地连边,容量为1,空地->羊连边,容量也为1。空地之间要连双向边,因为空地既可以属于S集,也可以属于T集。建完图就可以跑最大流了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define INF 100000007
using namespace std;
const int dx[5]={0,1,0,-1,0};
const int dy[5]={0,0,1,0,-1};
int head[10005],q[10005],dis[10005],list[500001],next[500001],key[500001],a[105][105];
int n,m,ans,xx,yy,cnt=1,T=10001;
void insert(int x,int y,int z)
{
next[++cnt]=head[x];
head[x]=cnt;
list[cnt]=y;
key[cnt]=z;
}
bool BFS()
{
int t=0,w=1,i,now;
memset(dis,-1,sizeof(dis));
q[0]=0; dis[0]=0;
while (t<w)
{
now=q[t]; t++; i=head[now];
while (i)
{
if (key[i]&&dis[list[i]]==-1)
{
dis[list[i]]=dis[now]+1;
q[w++]=list[i];
}
i=next[i];
}
}
return dis[T]==-1?0:1;
}
int dfs(int x,int f)
{
if (x==T) return f;
int w,used=0,i;
i=head[x];
while (i)
{
if (key[i]&&dis[list[i]]==dis[x]+1)
{
w=f-used;
w=dfs(list[i],min(w,key[i]));
key[i]-=w;
key[i^1]+=w;
used+=w;
if (used==f) return f;
}
i=next[i];
}
if (!used) dis[x]=-1;
return used;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
if (a[i][j]==1) { insert(0,(i-1)*m+j,INF); insert((i-1)*m+j,0,0); }
if (a[i][j]==2) { insert((i-1)*m+j,T,INF); insert(T,(i-1)*m+j,0); }
for (int k=1;k<=4;k++)
{
xx=i+dx[k];
yy=j+dy[k];
if (xx<1||xx>n||yy<1||yy>m||a[i][j]==2) continue;
if (a[i][j]!=1||a[xx][yy]!=1)
{
insert((i-1)*m+j,(xx-1)*m+yy,1);
insert((xx-1)*m+yy,(i-1)*m+j,0);
}
}
}
while (BFS()) ans+=dfs(0,INF);
printf("%d",ans);
return 0;
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: