LA 3661
題意是給出一張網格圖,每個點都延伸出橫邊,豎邊,斜邊,每條邊都有一條權值,要求左上角到右下角的道路被隔斷,問最小割。
對於最小割的題目,很自然地想到網絡流,但此題n的範圍並不小,用網絡流效率不高。針對本題的特點,我們可以將該圖進行轉化,轉化成其對偶圖,然後在對偶圖上求最短路即是原圖的最小割。
如何進行對偶圖轉化?原圖中的面—>新圖中的點,對於原圖中的每一條邊e,屬於兩個面f1,f2,那麼對應在新圖中一條邊(f1,f2),權值等於e的權值。
圖轉化完之後添加源點,匯點,建立源點到左邊界和下邊界的邊,權值爲0,建立上邊界和右邊界到匯點的邊,權值爲0。建圖完畢。
在對偶圖上跑一次Dijkstra,最短路距離即是最小割。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#define LL long long
using namespace std;
const int maxn=2e6+5;
const int Inf=0x3f3f3f3f;
struct HeapNode{
int u, d;
HeapNode(){}
HeapNode(int a,int b):u(a),d(b){}
bool operator < (const HeapNode& rhs)const{
return d>rhs.d;
}
};
struct Edge{
int to, next, d;
Edge(){}
Edge(int a,int b,int c):to(a),next(b),d(c){}
};
struct Dijkstra{
int n, m;
int ecnt;
Edge edges[3*maxn];
int head[maxn];
bool done[maxn];
int d[maxn];
int p[maxn];
void init(int n){
this->n=n;
memset(head,-1,sizeof(head));
ecnt=0;
}
void AddEdge(int u,int v,int w){
// printf("u=%d v=%d w=%d\n",u, v, w);
edges[ecnt]=Edge(v,head[u],w);
head[u]=ecnt++;
}
void dijkstra(int s){
priority_queue<HeapNode> Q;
for(int i=0;i<n;i++) d[i]=Inf;
d[s]=0;
memset(done,0,sizeof(done));
Q.push(HeapNode(s,0));
while(!Q.empty()){
HeapNode tNode=Q.top(); Q.pop();
int u=tNode.u;
if(done[u]) continue;
done[u]=true;
for(int i=head[u];i!=-1;i=edges[i].next){
Edge &e=edges[i];
if(d[e.to]>d[u]+e.d){
d[e.to]=d[u]+e.d;
p[e.to]=i;
Q.push(HeapNode(e.to,d[e.to]));
}
}
}
}
};
Dijkstra sol;
int T, n, m;
const int maxsize=1000;
int cost[maxsize][maxsize][3];
int ID(int r,int c,int f){
return f*n*m+r*m+c+1;
}
int main()
{
int d;
int cas=0;
while(~scanf("%d%d",&n,&m)){
if(n==0&&m==0) break;
for(int i=0;i<n;i++)
for(int j=0;j<m-1;j++){
scanf("%d",&cost[i][j][0]);
}
for(int i=0;i<n-1;i++)
for(int j=0;j<m;j++){
scanf("%d",&cost[i][j][1]);
}
for(int i=0;i<n-1;i++)
for(int j=0;j<m-1;j++){
scanf("%d",&cost[i][j][2]);
}
sol.init(2*n*m+2);
for(int i=0;i<n-1;i++)
for(int j=0;j<m-1;j++){
int id1=ID(i,j,0);
if(i<n-1) sol.AddEdge(id1,ID(i+1,j,1),cost[i+1][j][0]);
if(j>0) sol.AddEdge(id1,ID(i,j-1,1),cost[i][j][1]);
int id2=ID(i,j,1);
if(i>0) sol.AddEdge(id2,ID(i-1,j,0),cost[i][j][0]);
if(j<m-1) sol.AddEdge(id2,ID(i,j+1,0),cost[i][j+1][1]);
sol.AddEdge(id1,id2,cost[i][j][2]);
sol.AddEdge(id2,id1,cost[i][j][2]);
}
for(int i=0;i<n-1;i++){
sol.AddEdge(0,ID(i,0,0),cost[i][0][1]);
sol.AddEdge(ID(i,m-2,1),2*n*m+1,cost[i][m-1][1]);
}
for(int i=0;i<m-1;i++){
sol.AddEdge(0,ID(n-2,i,0),cost[n-1][i][0]);
sol.AddEdge(ID(0,i,1),2*n*m+1,cost[0][i][0]);
}
sol.dijkstra(0);
printf("Case %d: Minimum = %d\n",++cas, sol.d[2*n*m+1]);
}
return 0;
}