BZOJ1001 狼抓兔子(网络流转最短路:对偶图)

题意:


给一个如图形式的\(n*m\)的方格,从左上走到右下,给出边权,问分成两块所需的最小代价。\(n,m\leq1000\)

思路:

显然是个最小割,但是\(O(n^2m)\)的复杂度很高,虽然这道题能过。
这里介绍一种最大流改最短路的方法——对偶图。
对任意一个图我们可以变成对偶图:
如下图,每一个闭合的平面我们都给他标号,然后连接源点和汇点,把外面那个无穷大的平面分成两个平面\(s,t\)。然后开始新建边。新建边的每一条边为:把一条原来边的左右两个平面连接到一起,权值为原来的边的权值。可以得出最后的新建的边的数量和原来一样。最后跑\(s,t\)的最短路即可得出原图的最大流。

代码:

#include <map>
#include <set>
#include <queue>
#include <cmath>
#include <stack>
#include <ctime>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int maxn = 3e6 + 5;
const int MAXM = 3e6;
const ll MOD = 1e9 + 7;
const ull seed = 131;
const int INF = 0x3f3f3f3f;
struct Edge{
    int to, next;
    int w;
}edge[MAXM * 2];
struct qnode{
    int u;
    int c;
    qnode(int _u = 0, int _c = 0):u(_u), c(_c){}
    bool operator < (const qnode &r) const{
        return r.c < c;
    }
};
int tot, head[maxn], vis[maxn];
int dis[maxn];
void addEdge(int u, int v, int w){
    edge[tot].to = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}
void Dijkstra(int n, int st){
    memset(vis, 0, sizeof(vis));
    for(int i = 0; i <= n; i++) dis[i] = INF;
    priority_queue<qnode> que;
    while(!que.empty()) que.pop();
    dis[st] = 0;
    que.push(qnode(st, 0));
    qnode temp;
    while(!que.empty()){
        temp = que.top();
        que.pop();
        int u = temp.u;
        if(vis[u]) continue;
        vis[u] = 1;
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            int w = edge[i].w;
            if(!vis[v] && dis[v] > dis[u] + w){
                dis[v] = dis[u] + w;
                que.push(qnode(v, dis[v]));
            }
        }
    }
}
int n, m;
int getupid(int x, int y){
    return (x - 1) * (m - 1) + y;
}
int getdownid(int x, int y){
    return (x - 1) * (m - 1) + y + (n - 1) * (m - 1);
}
int main(){
    memset(head, -1, sizeof(head));
    tot = 0;
    scanf("%d%d", &n, &m);
    if(n == 1 || m == 1){
        int ans = INF;
        if(n == m) ans = 0;
        if(n < m) swap(n, m);
        for(int i = 1; i <= n - 1; i++){
            int w;
            scanf("%d", &w);
            ans = min(ans, w);
        }
        printf("%d\n", ans);
        return 0;
    }
    int st = 0, en = (n - 1) * (m - 1) * 2 + 1;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m - 1; j++){
            int w;
            scanf("%d", &w);
            if(i == 1){
                addEdge(st, getupid(i, j), w);
                addEdge(getupid(i, j), st, w);
            }
            else if(i == n){
                addEdge(en, getdownid(i - 1, j), w);
                addEdge(getdownid(i - 1, j), en, w);
            }
            else{
                addEdge(getupid(i, j), getdownid(i - 1, j), w);
                addEdge(getdownid(i - 1, j), getupid(i, j), w);
            }
        }
    }

    for(int i = 1; i <= n - 1; i++){
        for(int j = 1; j <= m; j++){
            int w;
            scanf("%d", &w);
            if(j == 1){
                addEdge(getdownid(i, j), en, w);
                addEdge(en, getdownid(i, j), w);
            }
            else if(j == m){
                addEdge(getupid(i, j - 1), st, w);
                addEdge(st, getupid(i, j - 1), w);
            }
            else{
                addEdge(getdownid(i, j), getupid(i, j - 1), w);
                addEdge(getupid(i, j - 1), getdownid(i, j), w);
            }
        }
    }

    for(int i = 1; i <= n - 1; i++){
        for(int j = 1; j <= m - 1; j++){
            int w;
            scanf("%d", &w);
            addEdge(getupid(i, j), getdownid(i, j), w);
            addEdge(getdownid(i, j), getupid(i, j), w);
        }
    }

    Dijkstra(en, st);
    printf("%d\n", dis[en]);

    return 0;
}

猜你喜欢

转载自www.cnblogs.com/KirinSB/p/11483826.html