[BZOJ 1001](BeiJingOI 2006)狼抓兔子

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 }

平面图最小割转对偶图最短路

时间: 2024-10-08 14:42:31

[BZOJ 1001](BeiJingOI 2006)狼抓兔子的相关文章

BZOJ 1001 Beijing 2006 狼抓兔子 最小割

题目大意:有一张无向图,描述的是兔子窝的位置和之间的边.现在狼来抓兔子了,兔子慌忙的从(1,1)逃走到(m,n).每条边上都有能通过最多的兔子数量.狼不想让兔子逃走,每在一条边驻守一只狼就可以避免一个兔子通过.问最少多少狼可以让所有兔子都不能逃走. 思路:建图,按题目中的意思是去掉最小的边使得源到汇不连通,显然的最小割. CODE: #include <queue> #include <cstdio> #include <cstring> #include <io

BZOJ 1001 [BJOI 2006] 狼抓兔子

BZOJ题面 瑾以此题纪念博主迈出冲刺省选的第一步 打眼一看,这不是裸的最小割吗? 最小割 == 最大流: 然后 5 分钟码完 ISAP : 交上去一看......TLE......懵逼...... 然后进行了一波没有卵用的优化常数...... (去他妈的 1e6 个点,3e6 条边!!!) 问了问度娘,才知道这题的图是 '平面图' : 平面图的最小割和最大流可以通过将其转换成对偶图跑最短路求解:(涨姿势) 那么什么是对偶图呢? 通俗的讲,对偶图就是把原图的点换成面(边围成的区域),面换成点,而

【BZOJ 1001】[BeiJing2006]狼抓兔子

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) 道路上的权值表示这条路上最多能够通过的兔子数,道路是

BZOJ 1001:[BeiJing2006]狼抓兔子(最小割)

http://www.lydsy.com/JudgeOnline/problem.php?id=1001 题意:中文. 思路:很明显是最小割,转化为最大流做.一开始看那么多点,但还是试了一下,居然过了.迷. 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <queue> 6 using namespac

解题:BJOI 2006 狼抓兔子

题面 可以看出来是最小割,然后你就去求最大流了 这么大的范围就是让你用网络流卡的?咋想的啊=.=??? 建议还是老老实实用 平面图最小割等于其对偶图最短路 这个东西来做吧,虽然这个东西跑的也挺慢的,最后一个点跑了$2s$ 对偶图就是被边分割出来的每个区域当成一个点,然后两个区域有公共边就连边,起点和终点的问题就在源汇点中间连一条边然后就能分出来了 1 #include<queue> 2 #include<cstdio> 3 #include<cctype> 4 #inc

ICPC-Beijing 2006 狼抓兔子

题目描述 题解: 裸的最小割. 但是最大流跑不过去怎么办? 转变一下,既然最大流是一条左下<->右上的通路,我们可以把图划分为若干区域, 最后找左下到右上的最短路就行了. 代码: #include<queue> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int N = 1100; t

bzoj1001 [ICPC-Beijing 2006]狼抓兔子

我满心以为本题正解为最短路,结果到处都是最大流-- 几乎所有的都写了什么"对偶图"跑最短路~~,但我真的不知道什么叫做对偶图~~-------------------------------------------------------------------------------------------------介绍一下本题的最短路算法叭.并不算难.主要是感性理解. 首先很容易观察出这是一个最小割,那么就是求最大流了. 但是这题的点数高达10e6,按常理来说最大流应该稳稳地TL

Luogu P4001 [ICPC-Beijing 2006]狼抓兔子

Link 平面图的最小割等于其对偶图的最短路. 虽然我不会证但感觉确实挺好理解的. #include<queue> #include<cstdio> #include<cctype> #include<vector> #include<cstring> #include<utility> #include<functional> #define pi pair<int,int> #define fi first

【BZOJ】1001: [BeiJing2006]狼抓兔子

1001: [BeiJing2006]狼抓兔子 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)的窝中去,狼王开始伏击