bzoj 1001狼抓兔子

1001: [BeiJing2006]狼抓兔子

Time Limit: 15 Sec   Memory Limit: 162 MB
Submit: 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

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




写题解之前先看一点有助于理解算法的东西:

对偶图的应用(转载自http://blog.sina.com.cn/lanbow)

0 定义

      一个图G=(V,E),若能将其画在平面上,且任意两条边的交点只能是G的顶点,则称G可嵌入平面,或称G是可平面的。可平面图在平面上的一个嵌入称为一个平面图。如下图左边黑色的图为平面图,右边红色的图不属于平面图:
对偶图的应用
由平面图的边包围而成,其中不含图的顶点。也称为面。包围面R的所有边组成的回路称为该面的边界,回路长度称为该面的度,记为deg(R)。具有相同边界的面称为相邻面。由平面图的边包围且无穷大的面称为外部面。一个平面图有且只有一个外部面。如下面的平面图中,R0是外部面R0与R1, R2, R3均相邻。deg(R0)=8, deg(R1)=4, deg(R2)=5(R2经过的顶点序列为v7-v4-v6-v4-v5-v7), deg(R3)=1:

对偶图的应用
利用欧拉公式和数学归纳法可以证明平面图G的所有面的度之和等于其边数|E|的2倍,即:
对偶图的应用
下面我们引入对偶图,设有平面图G=(V,E),满足下列条件的图G'= (V',E') 称为图G的对偶图:G的任一面Ri内有且仅有一点Vi';对G的域Ri和Rj的共同边界Ek,画一条边Ek'=(Vi',Vj')且只与Ek交于一点;若Ek完全处于Ri中,则Vi'有一自环Ek',如下图G'是G的对偶图:
对偶图的应用
1 最大流的应用
      如果网络流中的图G可以转化为一个平面图,那么其对偶图G'中的环对应G中的割,利用最大流最小割定理转化模型,根据平面图G'与其对偶图的关系,先求出最小割。首先连接s和t,如下图蓝色虚线,得到一个附加面,我们设附加面对应的点为s',无界面对应的点为t',求该图的红色的对偶图G',最后删去s'和t'之间的边:

对偶图的应用
一条从s'到t'的路径,就对应了一个s-t割,更进一步,如果我们令每条边的长度等于它的容量,那么最小割的容量就等于最短路的长度。这样时间复杂度大大降低了。


有了这个做铺垫,这道题就好想了。

数据范围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);
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值