【圖論專題二】【網路流部分】狼和羊的故事
【浙江省省選2009】狼和羊的故事
題目
【ZJOI2009】狼和羊的故事 (Standard IO)
Time Limits: 1000 ms Memory Limits: 256000 KB Detailed Limits
Description
“狼愛上羊啊愛的瘋狂,誰讓他們真愛了一場;狼愛上羊啊並不荒唐,他們說有愛就有方向......”
Orez聽到這首歌,心想:狼和羊如此和諧,為什麼不嘗試羊狼合養呢?說幹就幹!
Orez的羊狼圈可以看作一個n*m個矩陣格子,這個矩陣的邊緣已經裝上了籬笆。可是Drake很快發現狼再怎麼也是狼,它們總是對羊垂涎三尺,那首歌只不過是一個動人的傳說而已。所以Orez決定在羊狼圈中再加入一些籬笆,還是要將羊狼分開來養。
通過仔細觀察,Orez發現狼和羊都有屬於自己領地,若狼和羊們不能呆在自己的領地,那它們就會變得非常暴躁,不利於他們的成長。
Orez想要新增籬笆的儘可能的短。當然這個籬笆首先得保證不能改變狼羊的所屬領地,再就是籬笆必須修築完整,也就是說必須修建在單位格子的邊界上並且不能只修建一部分。
Input
輸入資料存放在文字檔案ws.in中。
檔案的第一行包含兩個整數n和m。接下來n行每行m個整數,1表示該格子屬於狼的領地,2表示屬於羊的領地,0表示該格子不是任何一隻動物的領地。
Output
輸出資料存放在文字檔案ws.out中。
檔案中僅包含一個整數ans,代表籬笆的最短長度。
Sample Input
2 2
2 2
1 1
Sample Output
2
Data Constraint
Hint
【資料範圍】
10%的資料 n,m≤3
30%的資料 n,m≤20
100%的資料 n,m≤100
題解
一眼看,這道題還以為是計算幾何。並且直接貪心即可。抱著將信將疑的態度打了一個對拍,發現有一個棘手的“0”。這個“0”會使答案很難計算。我們發現,這個空地要麼是屬於狼的,要麼是屬於羊的領地。所以我們可以考慮最小割。基本思路就是,設S為羊的領地,T為狼的領地。如果割得離S近,代表這個"0"屬於羊的領地,否則反之,然後跑一遍最小割最大流。
正解
我想不到其他方法了。故只有一種方法。
建一個原點S表示羊的領地;T點表示狼的領地。原先就屬於羊的領地就朝S點連一條邊權為無限大的邊;原先屬於狼的領地就朝T點同理。中間不確定的點就落在中間。將相鄰的點連起來,邊權為1。然後直接跑一遍最小割,就是答案。
你可以試這證明一下……
證明
首先,無限大的邊保證了原先屬於狼與羊的領地不會發生改變。而且,肯定會割掉中間那些關於籬笆的邊;對於不定項的點,如果割去左邊的邊,那麼就肯定是歸屬於羊了,否則反之。
程式碼
#include<cstdio>
#include<cstring>
#define N 101
#define M 101
using namespace std;
int total1,S,T,n,m;
int fx[5]={0,0,1,0,-1};
int fy[5]={0,1,0,-1,0};
int h[N*M*6],dis[N*M+2],head[N*M*6],next[N*M*6],edge[N*M*6],v[N*M*6];
int a[N][M];
void insert(int x,int y,int z)
{
total1++;
next[total1]=head[x];
head[x]=total1;
edge[total1]=y;
v[total1]=z;
}
bool bfs()
{
memset(dis,0,sizeof(dis));
int l=1,r=1;
h[1]=S;
dis[S]=1;
while (l<=r)
{
int u=h[l];
for (int i=head[u];i;i=next[i])
{
int y=edge[i];
if (v[i]>0&&dis[y]==0)
{
r++;
h[r]=y;
dis[y]=dis[u]+1;
if (y==T) return true;
}
}
l++;
}
return false;
}
int dinic(int k,int flow)
{
if (k==T) return flow;
int rest=flow;
for (int i=head[k];i;i=next[i])
{
int y=edge[i];
if (v[i]>0&&dis[y]==dis[k]+1)
{
int cost;
if (rest>v[i]) cost=v[i];
else cost=rest;
int get=dinic(y,cost);
if (get==0) dis[y]=0;
rest-=get;
v[i]-=get;
v[i^1]+=get;
if (rest==0) break;
}
}
return flow-rest;
}
int main()
{
scanf("%d%d",&n,&m);
total1=1;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
}
}
S=0;
T=n*m+1;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
{
if (a[i][j]==2)
{
insert(T,(i-1)*m+j,0);
insert((i-1)*m+j,T,999999);
//printf("%d %d %d\n",(i-1)*m+j,T,999999);
}
if (a[i][j]==1)
{
insert((i-1)*m+j,S,0);
insert(S,(i-1)*m+j,999999);
//printf("%d %d %d\n",S,(i-1)*m+j,999999);
}
for (int k=1;k<=4;k++)
{
int xx=i+fx[k];
int yy=j+fy[k];
if (xx>0&&yy>0&&xx<=n&&yy<=m)
{
if (a[i][j]==1)
{
if (a[xx][yy]!=a[i][j])
{
insert((i-1)*m+j,(xx-1)*m+yy,1);
insert((xx-1)*m+yy,(i-1)*m+j,0);
}
}
if (a[i][j]==0)
{
if (a[xx][yy]!=1)
{
insert((i-1)*m+j,(xx-1)*m+yy,1);
insert((xx-1)*m+yy,(i-1)*m+j,0);
}
}
}
}
}
}
//printf("%d\n",(total1-1)/2);
int maxflow=0;
int flow;
while (bfs())
while (flow=dinic(S,999999)) maxflow+=flow;
printf("%d",maxflow);
}