bzoj1001 [BeiJing2006]狼抓兔子
1001: [BeiJing2006]狼抓兔子
Time Limit: 15 Sec Memory Limit: 162 MBSubmit: 27126 Solved: 6933
[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
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新加数据一组,可能会卡掉从前可以过的程序。
分析:终于过了1001题!
这道题如果用最大流+优化也能过,但不是最优解.
注意这是一个平面图,平面图就是两条边的交点只会出现在顶点处. 平面图的最小割实际上就是对偶图两个对称点的最短路!
,如果两个平面之间有边,就将对应的两个平面连起来,边权为这两个平面之间的边的边权. 添加两个点1,N. 和图内部的平面连边.
最后求出1~N的最短路即可.
平面的编号有点麻烦. 我的代码是从上到下从左到右从2开始编号的,一个不容易错的思考方法:将点的下标看作是格子的下标,每一个格子里有两个平面,找找规律就行了.
注意边数的计算! 一个平面最多会向外连3条边,算上反向边,就有6条边.那么数组大小就要开到600w.
#include <cstdio> #include <queue> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 1010,inf = 0x7fffffff; int n,m,heng[maxn][maxn],shu[maxn][maxn],xie[maxn][maxn],N; int head[maxn * maxn * 2 + 10],to[6000010],nextt[6000010],tot = 1; int d[maxn * maxn * 2 + 10],vis[maxn * maxn * 2 + 10],w[6000010]; void add(int x,int y,int z) { w[tot] = z; to[tot] = y; nextt[tot] = head[x]; head[x] = tot++; } void spfa() { memset(d,127/3,sizeof(d)); queue <int> q; q.push(1); d[1] = 0; vis[1] = 1; while (!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for (int i = head[u];i;i = nextt[i]) { int v = to[i]; if (d[v] > d[u] + w[i]) { d[v] = d[u] + w[i]; if(!vis[v]) { vis[v] = 1; q.push(v); } } } } } void pre() { N = (n - 1) * (m - 1) * 2 + 2; for (int i = 1; i <= n; i++) for (int j = 1; j < m; j++) scanf("%d",&heng[i][j]); for (int i = 1; i < n; i++) for (int j = 1; j <= m; j++) scanf("%d",&shu[i][j]); for (int i = 1; i < n; i++) for (int j = 1; j < m; j++) scanf("%d",&xie[i][j]); } void solve() { pre(); for (int i = 1; i < n; i++) for (int j = 1; j < m; j++) { int temp = (i - 1) * (m - 1) + j; add(temp * 2,temp * 2 + 1,xie[i][j]); add(temp * 2 + 1,temp * 2,xie[i][j]); if (j != 1) { add(temp * 2 - 1,temp * 2,shu[i][j]); add(temp * 2,temp * 2 - 1,shu[i][j]); } if (i != 1) { int temp2 = (temp - m + 1); add(temp * 2 + 1,temp2 * 2,heng[i][j]); add(temp2 * 2,temp * 2 + 1,heng[i][j]); } } for (int i = 1; i < n; i++) { int temp = (i - 1) * (m - 1) + 1; add(1,temp * 2,shu[i][1]); add(temp * 2,1,shu[i][1]); temp = 2 * i * (m - 1) + 1; add(N,temp,shu[i][m]); add(temp,N,shu[i][m]); } for (int i = 1; i < m; i++) { int temp = i * 2 + 1; add(temp,N,heng[1][i]); add(N,temp,heng[1][i]); temp = (n - 2) * (m - 1) + i; add(temp * 2,1,heng[n][i]); add(1,temp * 2,heng[n][i]); } spfa(); printf("%d\n",d[N]); } int main() { scanf("%d%d",&n,&m); if (n == 1 && m == 1) printf("%d\n",0); else if (n == 1 || m == 1) { if (n == 1) { int minn = inf; for (int i = 1; i < m; i++) { int x; scanf("%d",&x); minn = min(minn,x); } printf("%d\n",minn); } else { int minn = inf; for (int i = 1; i < n; i++) { int x; scanf("%d",&x); minn = min(minn,x); } printf("%d\n",minn); } } else solve(); return 0; }