BZOJ 1001 [狼抓兔子]

题面

题意

  给定一张 \(n \times m\) 的类网格图,求左上角到右下角的最大流。

题解

  平面图上最大流转最短路。

  题意可转化为求左上角到右下角的最大流。根据最大流最小割定理,网络流中最大流的值等于最小割的容量。由于本题中给出的网络流是平面图,可以用最短路在几何意义上算出最小割:

  令题目给出的网络流图为 \(G\),源汇点分别是 \(s,t\)。从 \(t\)\(s\) 连一条流量无限的边 \(e_0\),令连边后 \(G\) 的对偶图为 \(G'\)\(s',t'\) 分别是 \(G\)\(e_0\) 分割的两个面在 \(G'\) 中对应的点,那么求 \(G\)\(s\)\(t\) 的最小割就可以等效为在 \(G'\) 中选取权值和尽可能小的边集,使得 \(s'\)\(t'\) 联通,也就是求 \(s'\)\(t'\) 的最短路。

  形象地说,令 \(e'\)\(e\) 的对应边,最小割的意义是割去权值和尽可能小的边使得 \(G\) 中没有 \(s\)\(t\) 的通路。而如果把在 \(G'\) 中选择 \(e'\) 看作 \(G\) 中 割去 \(e\),那么相当于找到 \(G'\)\(s'\)\(t'\) 的一条通路,把图 \(G\) 剪开。显然,权值和最小的通路就是 \(s'\)\(t'\) 的最短路。在 \(G'\) 中,点数和边数都是 \(O(nm)\) 的,Dijkstra 求解最短路,复杂度为 \(O(nm\log(nm))\)

代码

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
typedef long long ll;
const ll inf=1e18;
const int maxn=2e6+5,maxm=6e6+5;
int hea[maxn],nex[maxm],to[maxm],wei[maxm],tot;
struct node{
    int u; ll d;
    node(){}
    node(int u,ll d):u(u),d(d){}
};
bool operator < (const node &a,const node &b){
    return a.d>b.d;
}
ll dis[maxn];
priority_queue<node> que;
void dijkstra(int s,int n){
    int i,ed;
    int u,v,w;
    ll d;
    node tmp;
    for (i=0;i<n;i++) dis[i]=inf;
    while (!que.empty()) que.pop();
    que.push(node(s,dis[s]=0));
    while (!que.empty()){
        tmp=que.top(); que.pop(); u=tmp.u; d=tmp.d;
        if (d>dis[u]) continue;
        for (ed=hea[u];ed;ed=nex[ed]){
            v=to[ed]; w=wei[ed];
            if (dis[v]>d+w) que.push(node(v,dis[v]=(d+w)));
        }
    }
}
int n,m,s,t;
inline int getid(int x,int y,int z){
    if (x<1||y>=m) return s;
    if (x>=n||y<1) return t;
    return (x-1)*(m-1)*2+y*2-z;
}
void add(int u,int v,int w){
    tot++;
    nex[tot]=hea[u];
    hea[u]=tot;
    to[tot]=v;
    wei[tot]=w;
}
int main(){
    int i,j;
    int u,v,w;
    scanf("%d%d",&n,&m); s=0; t=(n-1)*(m-1)*2+1; tot=0;
    for (i=1;i<=n;i++) for (j=1;j<m;j++){
        scanf("%d",&w);
        u=getid(i-1,j,1); v=getid(i,j,0);
        add(u,v,w); add(v,u,w);
    }
    for (i=1;i<n;i++) for (j=1;j<=m;j++){
        scanf("%d",&w);
        u=getid(i,j-1,0); v=getid(i,j,1);
        add(u,v,w); add(v,u,w);
    }
    for (i=1;i<n;i++) for (j=1;j<m;j++){
        scanf("%d",&w);
        u=getid(i,j,1); v=getid(i,j,0);
        add(u,v,w); add(v,u,w);
    }
    dijkstra(s,t+1);
    printf("%lld\n",dis[t]);
    return 0;
}
posted @ 2019-11-29 02:26  Kilo-5723  阅读(173)  评论(0)    收藏  举报