1. 程式人生 > >【圖論專題二】【網路流部分】狼和羊的故事

【圖論專題二】【網路流部分】狼和羊的故事

【浙江省省選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); }