[BZOJ1001][BeiJing2006]狼抓兔子(最小割转最短路|平面图转对偶图)

1001: [BeiJing2006]狼抓兔子

Time Limit: 15 Sec  Memory Limit: 162 MB
Submit: 31805  Solved: 8494
[Submit][Status][Discuss]

Description

现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的,
而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个网格的地形:

 

左上角点为(1,1),右下角点为(N,M)(上图中N=4,M=5).有以下三种类型的道路 
1:(x,y)<==>(x+1,y) 
2:(x,y)<==>(x,y+1) 
3:(x,y)<==>(x+1,y+1) 
道路上的权值表示这条路上最多能够通过的兔子数,道路是无向的. 左上角和右下角为兔子的两个窝,
开始时所有的兔子都聚集在左上角(1,1)的窝里,现在它们要跑到右下解(N,M)的窝中去,狼王开始伏击
这些兔子.当然为了保险起见,如果一条道路上最多通过的兔子数为K,狼王需要安排同样数量的K只狼,
才能完全封锁这条道路,你需要帮助狼王安排一个伏击方案,使得在将兔子一网打尽的前提下,参与的
狼的数量要最小。因为狼还要去找喜羊羊麻烦.

Input

第一行为N,M.表示网格的大小,N,M均小于等于1000.
接下来分三部分
第一部分共N行,每行M-1个数,表示横向道路的权值. 
第二部分共N-1行,每行M个数,表示纵向道路的权值. 
第三部分共N-1行,每行M-1个数,表示斜向道路的权值. 
输入文件保证不超过10M

Output

输出一个整数,表示参与伏击的狼的最小数量.

Sample Input

3 4
5 6 4
4 3 1
7 5 3
5 6 7 8
8 7 6 5
5 5 5
6 6 6

Sample Output

14

HINT

 2015.4.16新加数据一组,可能会卡掉从前可以过的程序。

Solution

  一眼一个最小割。以为可以复习一下板子了,可以看这边数N*N*3不对呀,直接跑最小割会炸的。

  于是就翻题解学到了平面图转对偶图的神奇操作。

  平面图可以参考这篇博文 https://www.cnblogs.com/lfri/p/9939463.html

  平面图转对偶图的方法可以参考这篇博文 https://www.cnblogs.com/qzqzgfy/p/5578785.html

  像题目中这种源点和汇点在无界面的边界上的平面图叫做s-t平面图

  在这种图上可以实现求最小割转求最短路。

  需要注意的是实际操作时要先把s到t连条虚边,把原图的边界的面分成两个部分,也就是说多了一个附加面作为s。

  实现时主要是要给每个面(也就是对偶图中的点)编好号,代码中有注释。

Code

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pi;
//注意空间要开够 
const int N=1005*1005*2;
inline int read(){
    int x=0,w=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return w?-x:x;
}
struct edge{
    int v,w,last;
}e[N*3];
int tot,tail[N];
inline void add(int x,int y,int z){
    e[++tot]=(edge){y,z,tail[x]};
    tail[x]=tot;
    e[++tot]=(edge){x,z,tail[y]};
    tail[y]=tot;
}
int n,m,s,t,base;
//一个小正方形的下三角为(i-1)*(m-1)+j,上三角加个base
bool check(int i,int j){return i>=1&&i<=n-1&&j>=1&&j<=m-1;} 
int down(int i,int j){return check(i,j)?(i-1)*(m-1)+j:s;}
int up(int i,int j){return check(i,j)?down(i,j)+base:t;}
void build(){
    s=0,base=(n-1)*(m-1),t=base<<1|1;
    //原平面图的面有(n-1)*(m-1)*2+1个,再加个编号为0的附加面
    for(int i=1;i<=n;++i)
        for(int j=1;j<m;++j)
            add(down(i-1,j),up(i,j),read());
    for(int i=1;i<n;++i)
        for(int j=1;j<=m;++j)
            add(up(i,j-1),down(i,j),read());
    for(int i=1;i<n;++i)
        for(int j=1;j<m;++j)
            add(up(i,j),down(i,j),read());
}
//这只是一个普通的堆优化dijkstra 
bool vis[N];
int d[N];
void dij(){
    priority_queue<pi,vector<pi>,greater<pi> > q;
    memset(d,0x3f,sizeof d);d[s]=0;
    q.push(make_pair(d[s],s));
    while(!q.empty()){
        int x=q.top().second;q.pop();
        if(vis[x]) continue;vis[x]=true;
        for(int p=tail[x];p;p=e[p].last){
            int &y=e[p].v,&w=e[p].w;
            if(d[y]>d[x]+w){
                d[y]=d[x]+w;
                q.push(make_pair(d[y],y));
            }
        }
    }
}
int main(){
    n=read(),m=read();
    build();dij();
    cout<<d[t]<<endl;
    return 0;
}
BZOJ1001

 

posted @ 2019-07-26 17:54  青君  阅读(166)  评论(0编辑  收藏  举报