1. 程式人生 > >BJOI2006 狼抓兔子

BJOI2006 狼抓兔子

using else printf ble htm pan 等於 tdi second

題目描述

這道題可以看出來是最小割的板子題,不過因為這道題的n,m都到了1000,所以總點數是10^6,直接跑最小割會超時。

於是我們要新引入一個概念:對偶圖。

我們先說一下什麽是平面圖。平面圖就是所有的邊只在頂點處相交。

比如上面的圖就是一個平面圖。技術分享圖片

技術分享圖片

對於每一個平面圖,都有與之對應的對偶圖。平面圖中邊與邊之間圍成的區域叫面,最外面一個區域也被視作一個面。對偶圖就是把每個平面圖中的面都視為一個點,對於面與面之間的邊,就在面所對應的點之間建立一條雙向邊,邊權與原來相同。如果面的兩側都是自己,那麽就連一條自環的邊就可以了。

這樣我們就發現一條非常重要的性質,如果我們把原點和匯點連一條邊,把上半部分視為超級源點,下半部分視為超級匯點,那麽我們只要跑一遍對偶圖上的最短路,其數值大小即等於原圖最小割。直觀的理解一下,每條對偶圖上的邊都可以看作是在原圖上的一條杠,而如果想用這些杠把整個圖隔斷,(最小割)這些杠一定是可以連起來的。

(如果感到難懂可以看一下dalao的圖解):傳送門

所以只要轉化為對偶圖以後,跑一遍dij就可以了。

回來看這道題,是非常明顯的平面圖。所以我們把它的對偶圖建出來,直接跑最短路就可以了。

不過其實平面圖挺難建的……而且要註意的是,轉化為平面圖之後點有可能會變多,數組不要開小,預處理的時候不要忘記處理。

看一下代碼。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<set>
#include
<utility> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(‘\n‘) using namespace std; typedef long long ll; typedef pair<int,int> pr; const int INF = 1e9; const int M = 2000005; int read() { int ans = 0,op = 1; char ch = getchar();
while(ch < 0 || ch > 9) { if(ch == -) op = -1; ch = getchar(); } while(ch >= 0 && ch <= 9) { ans *= 10; ans += ch - 0; ch = getchar(); } return ans * op; } struct node { int next,to,v,from; } e[M<<3]; set <pr> q; set <pr> :: iterator it; int n,m,head[M<<1],maxflow = 0,d,dis[M<<1],ecnt,minn = INF; int source,sink; void add(int x,int y,int z) { e[++ecnt].to = y; e[ecnt].v = z; e[ecnt].from = x; e[ecnt].next = head[x]; head[x] = ecnt; } void build() { rep(i,1,n) { rep(j,1,m-1) { d = read(); if(i == 1) add(2*j,sink,d),add(sink,2*j,d); else if(i == n) add(j*2-1 + 2*(m-1)*(n-2),source,d),add(source,j*2-1 + 2*(m-1)*(n-2),d); else { add(j*2-1 + 2*(m-1)*(i-2),j*2 + 2*(m-1)*(i-1),d); add(j*2 + 2*(m-1)*(i-1),j*2-1 + 2*(m-1)*(i-2),d); } } } rep(i,1,n-1) { rep(j,1,m) { d = read(); if(j == 1) add(source,j + 2*(m-1)*(i-1),d),add(j+2*(m-1)*(i-1),source,d); else if(j == m) add(2*(m-1)*i,sink,d),add(sink,2*(m-1)*i,d); else { add(2*j-2 + 2*(m-1)*(i-1),2*j-1 + 2*(m-1)*(i-1),d); add(2*j-1 + 2*(m-1)*(i-1),2*j-2 + 2*(m-1)*(i-1),d); } } } int tot = -1; rep(i,1,n-1) { rep(j,1,m-1) { d = read(),tot += 2; add(tot,tot+1,d),add(tot+1,tot,d); } } } void dij(int s) { dis[s] = 0; q.insert(make_pair(dis[s],s)); while(!q.empty()) { pr now = *(q.begin()); q.erase(q.begin()); for(int i = head[now.second]; i; i = e[i].next) { if(dis[e[i].to] > dis[now.second] + e[i].v) { it = q.find(make_pair(dis[e[i].to],e[i].to)); if(it != q.end()) q.erase(it); dis[e[i].to] = dis[now.second] + e[i].v; q.insert(make_pair(dis[e[i].to],e[i].to)); } } } } int main() { n = read(),m = read(); if(n == 1) { rep(i,1,m-1) d = read(),minn = min(minn,d); printf("%d\n",minn); return 0; } if(m == 1) { rep(i,1,n-1) d = read(),minn = min(minn,d); printf("%d\n",minn); return 0; } source = 2*(n-1)*(m-1) + 1,sink = source + 1; build(); rep(i,1,M) dis[i] = INF; dis[source] = INF,dis[sink] = INF; dij(source); printf("%d\n",dis[sink]); return 0; }

BJOI2006 狼抓兔子