LA 3661

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;
}
發佈了54 篇原創文章 · 獲贊 3 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章