bzoj1001(洛谷P4001) - [Beijing2006]狼抓兔子

Author : hiang

bzoj1001     洛谷 P4001 

Time Limit: 15 Sec    Memory Limit: 162 MB

Description

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

 

左上角点为(1,1),右下角点为(N,M)(上图中N=3,M=4).有以下三种类型的道路 
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
 
 
这里提供两种思路:
一、最大流/最小割
学过网络流的不难看出这题符合最大流的条件,首先最重要的部分就是建图,下面提供建图方法:
一共有n行m列,所以我们可以将第一行的点设为1~m,第二行设为m+1~m*2,依此类推,可以得出起点是1,终点是n*m,按照输入的权值建边,注意因为是无向图,所以要建双向边,不然会WA,建完图跑最大流就可以了。
不过这题是网格图,边数较多,用朴素的dinic会TLE,所以加了一些玄学的优化
 
AC代码:
  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define MAXN 1001005
  4 #define MAXM 3004005
  5 #define inf 0x3f3f3f
  6 int n,m,s,t,num_edge=-1;
  7 int head[MAXN],cur[MAXN],dis[MAXN];
  8 struct Edge
  9 {
 10     int to,w,next;
 11 }edge[MAXM*2];
 12 inline int read()//快速读入
 13 {
 14    int s=0,w=1;
 15    char ch=getchar();
 16    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
 17    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
 18    return s*w;
 19 }
 20 void addedge(int from,int to,int w)
 21 {
 22     edge[++num_edge].next=head[from];
 23     edge[num_edge].to=to;
 24     edge[num_edge].w=w;
 25     head[from]=num_edge;
 26 }
 27 bool bfs()
 28 {
 29     memset(dis,0,sizeof(dis));
 30     for(int i=1;i<=n*m;i++)
 31         cur[i]=head[i];
 32     dis[s]=1;
 33     queue<int> q;
 34     q.push(s);
 35     while(!q.empty())
 36     {
 37         int u=q.front();
 38         q.pop();
 39         for(int i=head[u];i!=-1;i=edge[i].next)
 40         {
 41             if(dis[edge[i].to]==0&&edge[i].w)
 42             {
 43                 dis[edge[i].to]=dis[u]+1;
 44                 if(edge[i].to==t)
 45                     return 1;
 46                 q.push(edge[i].to);
 47             }
 48         }
 49     }
 50     return 0;
 51 }
 52 int dfs(int p,int limit)
 53 {
 54     if(p==t)
 55         return limit;
 56     int mi,used=0;
 57     for(int &i=cur[p];i!=-1;i=edge[i].next)//当前弧优化,非常省时
 58     {
 59         if(dis[edge[i].to]==dis[p]+1&&edge[i].w&&(mi=dfs(edge[i].to,min(edge[i].w,limit))))
 60         {
 61             if(mi)
 62             {
 63                 used+=mi;
 64                 limit-=mi;
 65                 edge[i].w-=mi;
 66                 edge[i^1].w+=mi;
 67                 if(!limit)
 68                     return used;
 69             }
 70             else
 71                 dis[p]=-1;//非常重要的优化,表示当前点无法再进行增广
 72         }
 73     }
 74     return used;
 75 }
 76 long long dinic()
 77 {
 78     long long ans=0;
 79     while(bfs())
 80         ans+=dfs(s,inf);
 81     return ans;
 82 }
 83 int main()
 84 {
 85     memset(head,-1,sizeof(head));
 86     int w,i,j;
 87     n=read();
 88     m=read();
 89     s=1;
 90     t=n*m;
 91     for(i=0;i<n;i++)//建横向边
 92     {
 93         for(j=1;j<=m-1;j++)
 94         {
 95             w=read();
 96             addedge(i*m+j,i*m+j+1,w);
 97             addedge(i*m+j+1,i*m+j,w);
 98         }
 99     }
100     for(i=0;i<n-1;i++)//建纵向边
101     {
102         for(j=1;j<=m;j++)
103         {
104             w=read();
105             addedge(i*m+j,(i+1)*m+j,w);
106             addedge((i+1)*m+j,i*m+j,w);
107         }
108     }
109     for(i=0;i<n-1;i++)//建斜向边
110     {
111         for(j=1;j<=m-1;j++)
112         {
113             w=read();
114             addedge(i*m+j,(i+1)*m+j+1,w);
115             addedge((i+1)*m+j+1,i*m+j,w);
116         }
117     }
118     printf("%lld",dinic());
119     return 0;
120 }

 二、平面图转对偶图 求最短路

先简单解释一下原理:

平面图:能画在平面上,且各边交点只能为顶点的图

对偶图:将平面图的各区域抽象成一个点,相邻区域之间连一条边,形成对偶图

以该题为例:

因为我们需要有一个起点和一个终点,所以我们要建立一个附加面,即s'所在的面,所得对偶图如上(建图方法不唯一)

可以发现,从s'到t'的任意一条路都是原图的割,于是这道题就转换成了求s'到t'的最短路,这里我用的spfa,居然比玄学的dinic还慢了一秒多......

dinic时间太玄学,但建图较简单,求最短路转换成对偶图后建图过程非常繁琐...但是较为保险

AC代码:

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define inf 0x3f3f3f3f
  4 const int MAXN=2001005;
  5 const int MAXM=6104005;
  6 int n,m,s,t,num_edge=-1;
  7 int head[MAXN];
  8 int dis[MAXN];
  9 bool inq[MAXN];
 10 struct Edge
 11 {
 12     int to,w,next;
 13 }edge[MAXM];
 14 inline int read()
 15 {
 16    int s=0,w=1;
 17    char ch=getchar();
 18    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
 19    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
 20    return s*w;
 21 }
 22 void addedge(int from,int to,int w)//邻接表建图
 23 {
 24     edge[++num_edge].next=head[from];
 25     edge[num_edge].to=to;
 26     edge[num_edge].w=w;
 27     head[from]=num_edge;
 28 }
 29 void spfa()
 30 {
 31     memset(dis,inf,sizeof(dis));
 32     memset(inq,0,sizeof(inq));
 33     dis[s]=0;
 34     queue<int> q;
 35     q.push(s);
 36     inq[s]=1;
 37     while(!q.empty())
 38     {
 39         int u=q.front();
 40         q.pop();
 41         inq[u]=0;
 42         for(int i=head[u];i!=-1;i=edge[i].next)
 43         {
 44             if(dis[edge[i].to]>dis[u]+edge[i].w)
 45             {
 46                 dis[edge[i].to]=dis[u]+edge[i].w;
 47                 if(!inq[edge[i].to])
 48                 {
 49                     q.push(edge[i].to);
 50                     inq[edge[i].to]=1;
 51                 }
 52             }
 53         }
 54     }
 55 }
 56 int main()
 57 {
 58     memset(head,-1,sizeof(head));
 59     int i,j,w,x,y;
 60     scanf("%d%d",&n,&m);
 61     s=(n-1)*(m-1)*2+1;//s不要设成0,亲测会WA
 62     t=(n-1)*(m-1)*2+2;
 63     for(i=1;i<=n;i++)//令人崩溃的建图
 64         for(j=1;j<m;j++)
 65         {
 66             w=read();
 67             x=(2*(i-1)-1)*(m-1)+j;
 68             y=2*(i-1)*(m-1)+j;
 69             if(i==1)
 70                 x=s;
 71             else if(i==n)
 72                 y=t;
 73             addedge(x,y,w);
 74             addedge(y,x,w);
 75         }
 76     for(i=1;i<n;i++)
 77         for(j=1;j<=m;j++)
 78         {
 79             w=read();
 80             x=2*(i-1)*(m-1)+j-1;
 81             y=(2*(i-1)+1)*(m-1)+j;
 82             if(j==1)
 83             {
 84                 x=(2*(i-1)+1)*(m-1)+j;
 85                 y=t;
 86             }
 87             else if(j==m)
 88             {
 89                 y=x;
 90                 x=s;
 91             }
 92             addedge(x,y,w);
 93             addedge(y,x,w);
 94         }
 95     for(i=1;i<n;i++)
 96         for(j=1;j<m;j++)
 97         {
 98             w=read();
 99             x=2*(i-1)*(m-1)+j;
100             y=(2*(i-1)+1)*(m-1)+j;
101             addedge(x,y,w);
102             addedge(y,x,w);
103         }
104     spfa();
105     printf("%d",dis[t]);
106     return 0;
107 }

 

posted @ 2019-06-07 17:11  CSGO_BEST_GAME_EVER  阅读(197)  评论(0编辑  收藏  举报