洛谷 P4001 [ICPC-Beijing 2006]狼抓兔子

题目描述

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

左上角点为(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只狼,才能完全封锁这条道路,你需要帮助狼王安排一个伏击方案,使得在将兔子一网打尽的前提下,参与的狼的数量要最小。因为狼还要去找喜羊羊麻烦。

输入格式

第一行为N,M.表示网格的大小,N,M均小于等于1000.

接下来分三部分

第一部分共N行,每行M-1个数,表示横向道路的权值.

第二部分共N-1行,每行M个数,表示纵向道路的权值.

第三部分共N-1行,每行M-1个数,表示斜向道路的权值.

输出格式

输出一个整数,表示参与伏击的狼的最小数量.

输入输出样例

输入 #1
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
输出 #1
14

思路:这个题是一道很显然的最小割,可以用最小割最大流定理去证明,最小割=最大流,然后用dinic解决该问题。然而我们看看数据范围,n,m<=1000,也就是说节点数量高达10^6,
很显然朴素的dinic是过不了的,然而因为
本题数据过于水,所以对dinic在加一些玄学优化也是能过得去的。然而,我这里要说一个对这类问题的一个最为有效的方法:平面图转对偶图。
对偶图的解释请参考相关书籍,这里不详细介绍。从题目中我们可以发现题目中给的图具有特殊性质,即每个方格都可以分成两个三角形,且这些方格与这些方格分成的三角形呈网格状排列,
这其实对于一个平面图来说,转换为其对应的对偶图是相对容易的,我们可以将原图中的起点S与终点T之间连一条线,这条线包围的面就作为对偶图的终点,而起点就是其全集的补。
建图比较复杂,写在main函数里面了。另外还要对n==1或m==1的情况进行特判。
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<queue>
  6 using namespace std;
  7 const int N = 2e6 + 10;
  8 const int M = 6e6 + 10;
  9 int read()
 10 {
 11     int ret = 0;
 12     char ch = getchar();
 13     while(ch < '0' || ch > '9') ch = getchar();
 14     while(ch >= '0' && ch <= '9')
 15     {ret = ret * 10 + ch - '0'; ch = getchar();}
 16     return ret;
 17 }
 18 struct edge{
 19     int to, nxt, dis;
 20 }e[M << 1];
 21 int head[N], tot = 0;
 22 void adde(int f, int t, int d)
 23 {
 24     e[++ tot] = (edge){t, head[f], d};
 25     head[f] = tot;
 26 }
 27 int n, m, st, ed;
 28 int dist[N], vis[N];
 29 struct node{
 30     int id, dis;
 31     bool operator < (const node A) const{
 32         return A.dis < dis;
 33     }
 34 };
 35 priority_queue <node> q;
 36 void dij()
 37 {
 38     memset(dist, 0x3f, sizeof dist);
 39     memset(vis, 0, sizeof vis);
 40     q.push((node){st, 0});
 41     dist[st] = 0;
 42     while(!q.empty())
 43     {
 44         int u = q.top().id;
 45         q.pop();
 46         if(vis[u]) continue;
 47         vis[u] = 1;
 48         for(int i = head[u]; i; i = e[i].nxt)
 49         {
 50             int v = e[i].to;
 51             if(dist[v] > dist[u] + e[i].dis)
 52             {
 53                 dist[v] = dist[u] + e[i].dis;
 54                 q.push((node){v, dist[v]});
 55             }
 56         }
 57     }
 58 }
 59 int val(int r, int c, int tmp)
 60 {
 61     return (r - 1) * (m - 1) + c + tmp * (m - 1) * (n - 1);
 62 }
 63 int main()
 64 {
 65     scanf("%d%d", &n, &m);
 66     //if(n == 1 || m == 1) {printf("1\n"); return 0;}
 67     int x;
 68     int minn = 0x3f3f3f3f;
 69     st = 0, ed = 2 * m * n + 1;
 70     for(int i = 1; i <= n; i ++)
 71         for(int j = 1; j <= m - 1; j ++)
 72         {
 73             scanf("%d", &x);
 74             minn = min(minn, x);
 75             if(i == 1)
 76             {
 77                 adde(val(i, j, 0), st, x);
 78                 adde(st, val(i, j, 0), x);
 79             }
 80             else if(i == n)
 81             {
 82                 adde(ed, val(i - 1, j, 1), x);
 83                 adde(val(i - 1, j, 1), ed, x);
 84             }
 85             else
 86             {
 87                 adde(val(i, j, 0), val(i - 1, j, 1), x);
 88                 adde(val(i - 1, j, 1), val(i, j, 0), x);
 89             }
 90         }
 91     for(int i = 1; i <= n - 1; i ++)
 92         for(int j = 1; j <= m; j ++)
 93         {
 94             scanf("%d", &x);
 95             minn = min(minn, x);
 96             if(j == 1)
 97             {
 98                 adde(ed, val(i, j, 1), x);
 99                 adde(val(i, j, 1), ed, x);
100             }
101             else if(j == m)
102             {
103                 adde(val(i, j - 1, 0), st, x);
104                 adde(st, val(i, j - 1, 0), x);
105             }
106             else
107             {
108                 adde(val(i, j, 1), val(i, j - 1, 0), x);
109                 adde(val(i, j - 1, 0), val(i, j, 1), x);
110             }
111         }
112     for(int i = 1; i <= n - 1; i ++)
113         for(int j = 1; j <= m - 1; j ++)
114         {
115             scanf("%d", &x);
116             adde(val(i, j, 0), val(i, j, 1), x);
117             adde(val(i, j, 1), val(i, j, 0), x);
118         }
119     dij();
120     if(n == 1 || m == 1) printf("%d\n", minn);
121     else printf("%d\n", dist[ed]);
122     return 0;
123 }

 

 
posted @ 2019-11-20 19:17  Frank喵^_^  阅读(273)  评论(1编辑  收藏  举报