BZOJ 1001 [BeiJing2006] 狼抓兔子

来源:互联网 发布:手机查看淘宝价格走势 编辑:程序博客网 时间:2024/06/16 05:01

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新加数据一组,可能会卡掉从前可以过的程序。

Source

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

最小割转最短路~

这道题直接用最小割会T得很惨(经验之谈)~

(如果好奇T了的程序长什么样,在这篇博文的最后面有~)

利用了对偶图的性质(并不知道是什么性质),可以把这道题中的图转化一下,再跑最短路。

具体做法是这样:


(红线是转成的图~图上有水印,我就不标来源了~)

新建一个源点,一个汇点,然后把每个由边围成的最小三角形看做点(我的标号是以每个方块左上角的点(u,v)为依据的,每个方块又被分为两个三角形,左下角的id是1,右上角的id是2,所以标号就是((u-1)*(m-1)+v-1)*2+id,建议画个图~),两个三角形之间连的边权值是两个三角形的公共边权值。然后SPFA就可以了~

注意n==1||m==1的情况要特判!不判的话WA到飞起啊!


#include<cstdio>#include<cstring>#include<iostream>#include<queue>using namespace std;#define d(u,v,id) (((u-1)*(m-1)+v-1)*2+id)int n,m,x,S,T,fi[2000005],w[6000005],ne[6000005],v[6000005],cnt,dis[2000005];bool b[2000005];void add(int u,int vv,int val){w[++cnt]=vv;ne[cnt]=fi[u];fi[u]=cnt;v[cnt]=val;w[++cnt]=u;ne[cnt]=fi[vv];fi[vv]=cnt;v[cnt]=val;}int spfa(){queue<int> q;memset(dis,127/3,sizeof(dis));dis[S]=0;b[S]=1;q.push(S);while(!q.empty()){int k=q.front();q.pop();b[k]=0;for(int i=fi[k];i;i=ne[i])  if(dis[w[i]]>dis[k]+v[i])  {  dis[w[i]]=dis[k]+v[i];  if(!b[w[i]])  {  b[w[i]]=1;q.push(w[i]);}  }}return dis[T];}int main(){scanf("%d%d",&n,&m);T=2*(n-1)*(m-1)+1;if(n==1 || m==1){if(n>m) swap(n,m);int ans=999999999;for(int i=1;i<m;i++) scanf("%d",&x),ans=min(x,ans);printf("%d\n",ans);return 0;}for(int i=1;i<m;i++) scanf("%d",&x),add(S,d(1,i,2),x);for(int i=2;i<n;i++)  for(int j=1;j<m;j++) scanf("%d",&x),add(d(i-1,j,1),d(i,j,2),x);for(int i=1;i<m;i++) scanf("%d",&x),add(d(n-1,i,1),T,x);for(int i=1;i<n;i++)  for(int j=1;j<=m;j++)  {  scanf("%d",&x);  if(j==1) add(d(i,j,1),T,x);  else if(j==m) add(S,d(i,j-1,2),x);  else add(d(i,j-1,2),d(i,j,1),x);  }for(int i=1;i<n;i++)  for(int j=1;j<m;j++) scanf("%d",&x),add(d(i,j,1),d(i,j,2),x);printf("%d\n",spfa());return 0;}



T了的最小割:


#include<cstdio>#include<cstring>#include<iostream>#include<queue>using namespace std;#define d(u,v) (u-1)*m+vint n,m,t,fi[1000001],w[6000001],ne[6000001],v[6000001],val,cnt,ans,dis[1000001];void add(int u,int vv,int val){w[++cnt]=vv;ne[cnt]=fi[u];fi[u]=cnt;v[cnt]=val;w[++cnt]=u;ne[cnt]=fi[vv];fi[vv]=cnt;v[cnt]=val;}bool bfs(){queue<int> q;q.push(1);memset(dis,-1,sizeof(dis));dis[1]=0;while(!q.empty()){int k=q.front();q.pop();for(int i=fi[k];i;i=ne[i])  if(v[i]>0 && dis[w[i]]==-1)  {  dis[w[i]]=dis[k]+1;q.push(w[i]);  }}if(dis[t]==-1) return 0;return 1;}int findd(int u,int vv){if(u==t) return vv;int kkz,tot=0;for(int i=fi[u];i;i=ne[i])  if(v[i]>0 && dis[w[i]]==dis[u]+1 && (kkz=findd(w[i],min(vv-tot,v[i]))))  {  v[i]-=kkz;v[i^1]+=kkz;tot+=kkz;  if(tot==vv) return tot;  }if(!tot) dis[u]=-1;return tot;}int main(){scanf("%d%d",&n,&m);cnt=1;t=d(n,m);for(int i=1;i<=n;i++)  for(int j=1;j<m;j++) scanf("%d",&val),add(d(i,j),d(i,j+1),val);for(int i=1;i<n;i++)  for(int j=1;j<=m;j++) scanf("%d",&val),add(d(i,j),d(i+1,j),val);for(int i=1;i<n;i++)  for(int j=1;j<m;j++) scanf("%d",&val),add(d(i,j),d(i+1,j+1),val);while(bfs()) ans+=findd(1,999999999);printf("%d\n",ans);return 0;}



1 0