这道算是比较正宗的对偶图了。。
题意很明显是求最小割,然后由最大流最小割定理又可以转化成最大流。。
然而如果直接跑ispa明显会tle,10^6个点呢。。
那最大流要怎么求呢。。这就是对偶图的一个经典应用了。。
http://blog.sina.com.cn/s/blog_60707c0f01011fnn.html
对偶图概念和在最大流上的应该这篇文章讲的还算蛮清楚,其实就是对偶图上的边代表了从哪阻断原图上的路径,边的权值代表阻断的代价,所以直接跑最短路就可以了。。
以后看到阻挡路径的问题可以往对偶图想想。。。
/**
* ┏┓ ┏┓
* ┏┛┗━━━━━━━┛┗━━━┓
* ┃ ┃
* ┃ ━ ┃
* ┃ > < ┃
* ┃ ┃
* ┃... ⌒ ... ┃
* ┃ ┃
* ┗━┓ ┏━┛
* ┃ ┃ Code is far away from bug with the animal protecting
* ┃ ┃ 神兽保佑,代码无bug
* ┃ ┃
* ┃ ┃
* ┃ ┃
* ┃ ┃
* ┃ ┗━━━┓
* ┃ ┣┓
* ┃ ┏┛
* ┗┓┓┏━┳┓┏┛
* ┃┫┫ ┃┫┫
* ┗┻┛ ┗┻┛
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<map>
#include<stack>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define eps 1e-12
#define succ(x) (1<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid (x+y>>1)
#define NM 2000005
#define nm 7000000
#define pi 3.1415926535897931
using namespace std;
const ll inf=1000000000000000000;
ll read(){
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f*x;
}
int n,m,id[1005][1005],tot,S,T;
ll d[NM];
struct edge{int t;ll v;edge*next;}e[nm],*h[NM],*o=e;
void _add(int x,int y,ll v){o->t=y;o->v=v;o->next=h[x];h[x]=o++;}
void add(int x,int y,ll v){_add(x,y,v);_add(y,x,v);}
struct tmp{
int x;
bool operator<(const tmp&o)const{return d[x]>d[o.x];}
};
void dij(){
priority_queue<tmp>q;
inc(i,1,T)d[i]=inf;
q.push(tmp{S});d[S]=0;
while(!q.empty()){
int t=q.top().x;q.pop();
link(t)if(d[j->t]>d[t]+j->v)d[j->t]=d[t]+j->v,q.push(tmp{j->t});
}
}
int main(){
freopen("data.in","r",stdin);
n=read();m=read();
if(n==1&&m==1)return 0*printf("0\n");
if(n==1||m==1){
int ans=read();
while(~scanf("%d",&n))ans=min(ans,n);
return 0*printf("%d\n",ans);
}
inc(i,1,n-1)inc(j,1,m-1)id[i][j]=++tot;
S=tot<<1|1;T=S+1;
inc(i,1,m-1)add(S,id[1][i],read());
inc(i,1,n-2)inc(j,1,m-1)add(id[i][j]+tot,id[i+1][j],read());
inc(j,1,m-1)add(id[n-1][j]+tot,T,read());
inc(i,1,n-1){
add(id[i][1]+tot,T,read());
inc(j,1,m-2)add(id[i][j],id[i][j+1]+tot,read());
add(id[i][m-1],S,read());
}
inc(i,1,n-1)inc(j,1,m-1)add(id[i][j],id[i][j]+tot,read());
dij();
printf("%lld\n",d[T]);
return 0;
}
1001: [BeiJing2006]狼抓兔子
Time Limit: 15 Sec Memory Limit: 162 MBSubmit: 26913 Solved: 6858
[ 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
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新加数据一组,可能会卡掉从前可以过的程序。