bzoj1001(洛谷P4001) - [Beijing2006]狼抓兔子
Author : hiang
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
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 }