UVa1376/LA3661 Animal Run

题目链接

   UVA - 1376 Animal Run

题意

   由于控制程序出了 bug,动物园的笼子无缘无故被打开,所有动物展开了一次大逃亡。整个城市是一个网格,另外每个单位方格都有一条从左上到右下的对角线,其中动物园在左上角,动物们的目的地是右下角。所有道路(即网格的边和对角线)都是双向的。
   每条道路上都有一个正整数权值,代表拦截这条边所需要的工作人员数,如下图所示。你的任务是派尽量少的工作人员,使动物无法从动物园走到目的地(动物只能经过没有被拦截的边)。Animal Run

输入格式

   输入包含多组数据。每组数据第一行为两个整数n 和m(3≤n,m≤1 000),即网格的行数和列数。以下n 行每行m-1个整数,即拦截每条横边所需的人数。以下n-1行每行有m个整数,即拦截每条竖边所需的人数。以下n-1行每行m-1个整数,表示拦截每条对角线边所需的人数。上述边的排列顺序为从上到下从左到右。所有权值均为不超过 1 0 6 10^6 106的正整数。输入结束标志为n=m=0。输入文件大小约为16MB。

输出格式

   对于每组数据,输出需要派遣的工作人员总数。

分析

   求平面图的最小割,但本题数据大,如果直接跑最大流估计会tle/mle。
   利用对偶图,求平面图最小割可转化成求对偶图最短路。
   转化成对偶图的办法参见:最小割之平面图转最短路对偶图对于平面图最小割的求解
   先将原s和t连一条边,这样会多出来一个面,叫做附加面,将这个附加面作为对偶图的 s ∗ s* s,将无限面作为对偶图的 t ∗ t* t s ∗ s* s t ∗ t* t以及原图的每个面构成对偶图的顶点,某两个面如果共边,则他们之间连一条边。注意 s ∗ s* s t ∗ t* t的边要删除,这样就构建好了对偶图。求原图的最小割就转化成了求 s ∗ s* s t ∗ t* t的最短路。
对偶图

AC 代码

#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;

#define N 1002
int d[2*N*N], f[2*N*N], m, n, kase = 0; struct edge {int v, w;}; vector<edge> g[2*N*N];
struct node {
    int d, u;
    bool operator< (const node& rhs) const {
        return d>rhs.d;
    }
};

void solve() {
    int t = 2*(m-1)*(n-1)+1;
    for (int i=0; i<=t; ++i) g[i].clear();
    for (int i=0; i<n; ++i) for (int j=1; j<m; ++j) {
        int u = i<n-1 ? 2*(i*(m-1)+j) : t, v = i ? 2*((i-1)*(m-1)+j)-1 : 0, w; cin >> w;
        g[u].push_back({v, w}); g[v].push_back({u, w});
    }
    for (int i=1; i<n; ++i) for (int j=0; j<m; ++j) {
        int u = j ? 2*((i-1)*(m-1)+j) : t, v = j<m-1 ? 2*((i-1)*(m-1)+j)+1 : 0, w; cin >> w;
        g[u].push_back({v, w}); g[v].push_back({u, w});
    }
    for (int i=1; i<n; ++i) for (int j=1; j<m; ++j) {
        int u = 2*((i-1)*(m-1)+j), v = u-1, w; cin >> w;
        g[u].push_back({v, w}); g[v].push_back({u, w});
    }
    memset(d, 0x7f, sizeof(d)); memset(f, 0, sizeof(f));
    d[0] = 0; priority_queue<node> q; q.push({0, 0});
    while (!q.empty()) {
        int u = q.top().u; q.pop();
        if (u == t) break;
        if (f[u]) continue;
        f[u] = 1;
        for (int i=g[u].size()-1; i>=0; --i) {
            int v = g[u][i].v, d1 = d[u] + g[u][i].w;
            if (d[v] > d1) d[v] = d1, q.push({d[v], v});
        }
    }
    cout << "Case " << ++kase << ": Minimum = " << d[t] << endl;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    while (cin >> n >> m && (n || m)) solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值