1001: [BeiJing2006]狼抓兔子
Time Limit: 15 Sec Memory Limit: 162 MBSubmit: 15622 Solved: 3771
[ 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
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
HINT
2015.4.16新加数据一组,可能会卡掉从前可以过的程序。
写题解之前先看一点有助于理解算法的东西:
对偶图的应用(转载自http://blog.sina.com.cn/lanbow)
0 定义
数据范围1000*1000 如果直接跑最大流,赤裸裸的超时了,所以要用到平面图转对偶图的思想。因为最大流等于最小割,那么我们只有求出最小割即可,建图方式如下:
s,t 分别表示起点和终点。把每个三角形缩成一个点,然后连边,保证原图中的每一条边,都与对偶图中的边相交于一点,边的权值及为原图中被割断的边的权值,表示割断他的代价,然后就可以用最短路跑最小割,然后就可以解决了。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m,tot=-1,t;
int point[2000100],next[8000000],v[8000000],remain[8000000],dis[2000100];
int q[2100000],can[2000100];
void add(int x,int y,int z)
{
tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z;
tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=z;
}
void spfa(int s,int t)
{
memset(dis,0x7f,sizeof(dis));
dis[s]=0; can[s]=1;
int head=0; int tail=1; q[1]=s;
int p=2010000;
while (head!=tail)
{
head++;
head=(head-1)%p+1;
int now=q[head];
for (int i=point[now];i!=-1;i=next[i])
if (dis[v[i]]>dis[now]+remain[i])
{
dis[v[i]]=dis[now]+remain[i];
if (can[v[i]]==0)
{
tail++;
tail=(tail-1)%p+1;
q[tail]=v[i];
can[v[i]]=1;
}
}
can[now]=0;
}
printf("%d",dis[t]);
return;
}
int main()
{
scanf("%d%d",&n,&m);
memset(next,-1,sizeof(next));
memset(point,-1,sizeof(point));
if (n==1&&m==1)
{
printf("1\n"); return 0;
}
if (n==1)
{
int minn=1000000000;
for (int j=1;j<=m-1;j++)
{
int x; scanf("%d",&x);
minn=min(minn,x);
}
printf("%d",minn);
return 0;
}
if (m==1)
{
int minn=1000000000;
for (int i=1;i<=n-1;i++)
for (int j=1;j<=m;j++)
{
int x; scanf("%d",&x);
minn=min(minn,x);
}
printf("%d\n",minn);
return 0;
}
t=(n-1)*(m-1)*2+1;
for (int i=1;i<=n;i++)
for (int j=1;j<=m-1;j++)
{
int x; scanf("%d",&x);
if (i==1){ add(j,t,x); continue;}
if (i==n){ add(0,2*(i-2)*(m-1)+j+m-1,x); continue; }
int kk=m-1+j;
add(kk+(i-2)*2*(m-1),kk+(i-2)*2*(m-1)+m-1,x);
}
for (int i=1;i<=n-1;i++)
for (int j=1;j<=m;j++)
{
int x; scanf("%d",&x);
if (j==1){ add(0,m+(i-1)*(m-1)*2,x); continue;}
if (j==m){ add(m-1+(i-1)*(m-1)*2,t,x); continue; }
add(j-1+(m-1)*(i-1)*2,j-1+(i-1)*(m-1)*2+m,x);
}
for (int i=1;i<=n-1;i++)
for (int j=1;j<=m-1;j++)
{
int x; scanf("%d",&x);
add(j+(i-1)*(m-1)*2,j+(i-1)*(m-1)*2+m-1,x);
}
spfa(0,t);
}