BZOJ 1001: [BeiJing2006]狼抓兔子最大流/SPFA+最小割,多解

Posted Angel_Kitty

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 1001: [BeiJing2006]狼抓兔子最大流/SPFA+最小割,多解相关的知识,希望对你有一定的参考价值。

1001: [BeiJing2006]狼抓兔子

Time Limit: 15 Sec  Memory Limit: 162 MB
Submit: 23822  Solved: 6012
[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新加数据一组,可能会卡掉从前可以过的程序。

Source

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1001

分析:最大流写法如下:

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 typedef long long ll;
  4 int n,m;
  5 inline int read()
  6 {
  7     int x=0,f=1;
  8     char ch=getchar();
  9     while(ch<0||ch>9)
 10     {
 11         if(ch==-)
 12             f=-1;
 13         ch=getchar();
 14     }
 15     while(ch>=0&&ch<=9)
 16     {
 17         x=x*10+ch-0;
 18         ch=getchar();
 19     }
 20     return x*f;
 21 }
 22 inline void write(int x)
 23 {
 24     if(x<0)
 25     {
 26         putchar(-);
 27         x=-x;
 28     }
 29     if(x>9)
 30     {
 31         write(x/10);
 32     }
 33     putchar(x%10+0);
 34 }
 35 int ne;
 36 const int N=1000010;
 37 struct data
 38 {
 39     int to,next,v;
 40 }e[N<<3];
 41 int head[N];
 42 int h[N],q[N],ans;
 43 inline void update(int u,int v,int w)
 44 {
 45     ne++;
 46     e[ne].to=v;
 47     e[ne].v=w;
 48     e[ne].next=head[u];
 49     head[u]=ne;
 50 }
 51 inline bool BFS()
 52 {
 53     int now,i;
 54     memset(h,-1,sizeof(h));
 55     int t=0,w=1;
 56     q[t]=1;
 57     h[1]=0;
 58     while(t<w)
 59     {
 60         now=q[t];
 61         t++;
 62         i=head[now];
 63         while(i)
 64         {
 65             if(e[i].v&&h[e[i].to]<0)
 66             {
 67                 q[w++]=e[i].to;
 68                 h[e[i].to]=h[now]+1;
 69             }
 70             i=e[i].next;
 71         }
 72     }
 73     if(h[n*m]==-1)
 74         return false;
 75     return true;
 76 }
 77 inline int DFS(int x,int f)
 78 {
 79     if(x==n*m)
 80         return f;
 81     int i=head[x];
 82     int w,used=0;
 83     while(i)
 84     {
 85         if(e[i].v&&h[e[i].to]==h[x]+1)
 86         {
 87             w=f-used;
 88             w=DFS(e[i].to,min(w,e[i].v));
 89             e[i].v-=w;
 90             e[i+1].v+=w;
 91             used+=w;
 92             if(used==f)
 93                 return f;
 94         }
 95         i=e[i].next;
 96     }
 97     if(!used)
 98         h[x]=-1;
 99     return used;
100 }
101 inline void dinic()
102 {
103     while(BFS())
104     {
105         ans+=DFS(1,0x7f7f7f7f);
106     }
107 }
108 int main()
109 {
110     n=read();
111     m=read();
112     int x;
113     for(int i=1;i<=n;i++)
114     {
115         for(int j=1;j<m;j++)
116         {
117             x=read();
118             update(m*(i-1)+j,m*(i-1)+j+1,x);
119             update(m*(i-1)+j+1,m*(i-1)+j,x);
120         }
121     }
122     for(int i=1;i<n;i++)
123     {
124         for(int j=1;j<=m;j++)
125         {
126             x=read();
127             update(m*(i-1)+j,m*(i)+j,x);
128             update(m*(i)+j,m*(i-1)+j,x);
129         }
130     }
131     for(int i=1;i<n;i++)
132     {
133         for(int j=1;j<m;j++)
134         {
135             x=read();
136             update(m*(i-1)+j,m*(i)+j+1,x);
137             update(m*(i)+j+1,m*(i-1)+j,x);
138         }
139     }
140     dinic();
141     write(ans);
142     return 0;
143 }

分析:最小割,上网看了别人的博客,学习到了s-t平面图的最小割的解法,把原图中的面看作点,起点和终点都等同于最外面的那一个面,原图中一条边权值为w,新图中就等同于此边在平面图中分割开的两个面(即新图中两个点)连一条边,权值为w。建模完成后,新图中的起点和终点的一条路径就穿插过原图的一些边,即一条路径等于原图中的一个割,所以最小割就等于新图的最短路径长度。确实很厉害。

推荐文章:浅析最大最小定理在信息学竞赛中的应用》--周冬

下面给出SPFA+最小割代码:

 

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 typedef long long ll;
  4 inline int read()
  5 {
  6     int x=0,f=1;
  7     char ch=getchar();
  8     while(ch<0||ch>9)
  9     {
 10         if(ch==-)
 11             f=-1;
 12         ch=getchar();
 13     }
 14     while(ch>=0&&ch<=9)
 15     {
 16         x=x*10+ch-0;
 17         ch=getchar();
 18     }
 19     return x*f;
 20 }
 21 inline void write(int x)
 22 {
 23     if(x<0)
 24     {
 25         putchar(-);
 26         x=-x;
 27     }
 28     if(x>9)
 29     {
 30         write(x/10);
 31     }
 32     putchar(x%10+0);
 33 }
 34 #define M 2000001
 35 int n,m,nm;
 36 struct data
 37 {
 38     int to,next,v;
 39 }e[4*M];
 40 int dis[M],q[M],head[M];
 41 bool flag[M];
 42 int ne;
 43 inline void update(int u,int v,int w)
 44 {
 45     ne++;
 46     e[ne].to=v;
 47     e[ne].v=w;
 48     e[ne].next=head[u];
 49     head[u]=ne;
 50     ne++;
 51     e[ne].to=u;
 52     e[ne].v=w;
 53     e[ne].next=head[v];
 54     head[v]=ne;
 55 }
 56 inline void spfa()
 57 {
 58     memset(dis,0x3f,sizeof(dis));
 59     int i,t=0,w=1;
 60     dis[0]=q[w]=0;flag[0]=1;
 61     while(t!=w)
 62     {
 63         int u=q[t++];
 64         flag[u]=0;
 65         if(t==M)t=0;
 66         for(int i=head[u];i;i=e[i].next)
 67         {
 68             int v=e[i].to;
 69             if(dis[v]>dis[u]+e[i].v)
 70             {
 71                 dis[v]=dis[u]+e[i].v;
 72                 if(flag[v]==0)
 73                 {
 74                     flag[v]=1;
 75                     q[w++]=v;
 76                     if(w==M)w=0;
 77                 }
 78             }
 79         }
 80     }
 81 }
 82 int main()
 83 {
 84     n=read();
 85     m=read();
 86     nm=(n*m-m-n+1)<<1;
 87     int x;
 88     for(int j=1;j<m;j++)
 89     {
 90         x=read();
 91         update(j,nm+1,x);
 92     }
 93     for(int i=1;i<n-1;i++)
 94     {
 95         for(int j=1;j<m;j++)
 96         {
 97             x=read();
 98             update((i<<1)*(m-1)+j,((i<<1)-1)*(m-1)+j,x);
 99         }
100     }
101     for(int j=1;j<m;j++)
102     {
103         x=read();
104         update(0,((n<<1)-3)*(m-1)+j,x);
105     }
106     for(int i=0;i<n-1;i++)
107     {
108         for(int j=1;j<=m;j++)
109         {
110             x=read();
111             if(j==1)
112                 update(0,(i<<1)*(m-1)+m,x);
113             else if(j==m)
114                 update((i<<1|1)*(m-1),nm+1,x);
115             else
116                 update((i<<1)*(m-1)+j-1,(i<<1)*(m-1)+j+m-1,x);
117         }
118     }
119     for(int i=0;i<n-1;i++)
120     {
121         for(int j=1;j<m;j++)
122         {
123             x=read();
124             update((i<<1|1)*(m-1)+j,(i<<1)*(m-1)+j,x);
125         }
126     }
127     spfa();
128     write(dis[nm+1]);
129     return 0;
130 }

 

以上是关于BZOJ 1001: [BeiJing2006]狼抓兔子最大流/SPFA+最小割,多解的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 1001: [BeiJing2006]狼抓兔子

bzoj 1001 [BeiJing2006]狼抓兔子

Bzoj1001 [BeiJing2006]狼抓兔子

bzoj1001 [BeiJing2006]狼抓兔子

BZOJ1001 [Beijing2006]狼抓兔子

Bzoj1001 [BeiJing2006]狼抓兔子