Description Source: Beijing2006 [BJOI2006]
八中OJ上本题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1001
现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的,而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个网格的地形:
左上角点为(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
【分析】
乍一看题,心想,bzoj似乎也不过如此啊,这不是最小割裸题吗?不要急,我们来看看数据范围……N,M均小于等于1000,而节点总数为MN……请告诉我比$O(VE)$ 更优的网络流怎么写= =
很快我们就可以发现,根据这道题目的特殊性,我们得到的是一张平面图。图论告诉我们,每张平面图都有与之相对应的对偶图,而原图的“割”与它的对偶图的”路径“一一对应(详见周冬的论文《两极相同——浅析最大最小定理在信息学竞赛中的应用》)。于是我们可以把网格中的点和面互换,再把从S到T的连线上下两侧分别拆成两个点,求这两个点之间的最短路即可。
(我的代码没有利用图的“边数有限”这一特点,还是用一个vector储存了邻接表,在有些OJ上可能会超时。
1 /**************************************************************
2 Problem: 1001
3 User: 935671154
4 Language: C++
5 Result: Accepted
6 Time:8536 ms
7 Memory:104700 kb
8 ****************************************************************/
9
10 #include <iostream>
11 #include <cctype>
12 #include <cstdio>
13 #include <vector>
14 #include <algorithm>
15 #include <cmath>
16 #include <queue>
17 using namespace std;
18 inline void getd(int &x){
19 char c = getchar();
20 bool minus = 0;
21 while(!isdigit(c) && c != ‘-‘)c = getchar();
22 if(c == ‘-‘)minus = 1, c = getchar();
23 x = c - ‘0‘;
24 while(isdigit(c = getchar()))x = x * 10 + c - ‘0‘;
25 if(minus)x = -x;
26 }
27 /*======================================================*/
28 const int maxn = 2000003, INF = 0x3f3f3f3f;
29 #define pb push_back
30 struct edge{
31 int to, w;
32 edge(int t, int W):to(t), w(W){}
33 };
34 vector<edge> adj[maxn];
35 int N, M, To, dis[maxn] = {0};
36
37 inline bool init(){
38 int i, j, k, t, d;
39 getd(N), getd(M);
40 if(N == 1){
41 int ans = INF;
42 for(i = 1;i < M;++i)
43 getd(t), ans = min(ans, t);
44 printf("%d\n", ans);
45 return 0;
46 }
47 if(M == 1){
48 int ans = INF;
49 for(i = 1;i < N;++i)
50 getd(t), ans = min(ans, t);
51 printf("%d\n", ans);
52 return 0;
53 }
54 To = ((N-1) * (M-1) * 2) + 1;
55 //横向道路
56 d = 2 * M - 3;
57 for(i = k = 1;i < M;++i, k+=2){
58 getd(t);
59 adj[0].pb(edge(k, t));
60 }
61 for(i = 2;i < N;++i){
62 for(j = 1;j < M;++j, k+=2){
63 getd(t);
64 adj[k].pb(edge(k-d, t));
65 adj[k-d].pb(edge(k, t));
66 }
67 }
68 for(j = 1, k = To-2*(M-2)-1;j < M;++j, k+=2){
69 getd(t);
70 adj[k].pb(edge(To, t));
71 }
72 //纵向道路
73 for(i = 1,k = 2;i < N;++i){
74 getd(t);
75 adj[k].pb(edge(To, t));
76 for(j = 2,k+=2;j < M;++j,k+=2){
77 getd(t);
78 adj[k].pb(edge(k-3, t));
79 adj[k-3].pb(edge(k, t));
80 }
81 getd(t);
82 adj[0].pb(edge(k-3, t));
83 }
84 //斜向道路
85 for(i = 1, k = 1;i < N;++i){
86 for(j = 1;j < M;++j, k += 2){
87 getd(t);
88 adj[k].pb(edge(k+1, t));
89 adj[k+1].pb(edge(k, t));
90 }
91 }
92 for(i = 1;i <= To;++i)
93 dis[i] = INF;
94 return 1;
95 }
96 queue<int> Q;
97 bool inQ[maxn] = {1};
98 inline void work(){
99 Q.push(0);
100 int t;
101 vector<edge>::iterator it;
102 while(!Q.empty()){
103 t = Q.front();Q.pop();inQ[t] = 0;
104 for(it = adj[t].begin();it != adj[t].end();++it)
105 if(dis[t] + it->w < dis[it->to]){
106 dis[it->to] = dis[t] + it->w;
107 if(!inQ[it->to])
108 Q.push(it->to), inQ[it->to] = 1;
109 }
110 }
111 printf("%d\n", dis[To]);
112 }
113
114 int main(){
115 #if defined DEBUG
116 freopen("test", "r", stdin);
117 #endif
118
119 if(init())
120 work();
121
122 #if defined DEBUG
123 cout << endl << (double)clock()/CLOCKS_PER_SEC << endl;
124 #endif
125 return 0;
126 }
平面图最小割转对偶图最短路