POJ 3308 Paratroopers (最小点权覆盖Dinic)
2011-10-23 22:59
555 查看
题意:有些火星人要来袭击地球,我们知道这些火星人登陆的坐标(row,col),现在要把他们全部KO。地球上有一种激光武器每次可以消灭一行或者一列中所有的火星人。但是不同的行以及不同的列上安置武器的花费不一样。并且总花费为每一行每一列的花费的乘积。现在要求在最小花费的情况下消灭所有火星人。
题解:最小点权覆盖,最小割。求乘积则换成log。给出了两组代码。第一组与EK算法有些相似的地方,可以结合着看,第二组跟简洁一些。。
下面的代码更简洁···
题解:最小点权覆盖,最小割。求乘积则换成log。给出了两组代码。第一组与EK算法有些相似的地方,可以结合着看,第二组跟简洁一些。。
#include <cmath> #include <iostream> using namespace std; #define MAX 1000 #define eps 0.0001 #define INF 9999999999 #define min(a,b) (a<b?a:b) struct Edge { int st, ed; int next; double flow; } edge[MAX*10]; int pre[MAX]; // pre[u]储存以u为终点的边的编号,它储存的是边 int head[MAX], out[MAX]; /* 每次跟新out[u]都相当于把点u所关联的下一条边存储起来,这样就可以避免重复地由head[u]开始一个一个找。 有的写法没有用out[]这个数组,而是标记出所有被删除的边,所以增加了一个数组del[], 这样写貌似会浪费一些时间, 因为每次都需要从某个点的第一条边逐步地找,直到找到合适的边。*/ int leve[MAX]; // 层次图中每个点的层次 int que[MAX], stk[MAX]; int E, R, C, L, src, dest; void add_edge ( int u, int v, double val ) { edge[E].st = u; edge[E].ed = v; edge[E].flow = val; edge[E].next = head[u]; head[u] = E++; edge[E].st = v; edge[E].ed = u; edge[E].flow = 0; edge[E].next = head[v]; head[v] = E++; } bool dinic_bfs () { int front, rear, u, v, i; front = rear = 0; memset(leve,-1,sizeof(leve)); que[rear++] = src; leve[src] = 0; while ( front != rear ) { u = que[front]; front = ( front + 1 ) % MAX; for ( i = head[u]; i != -1; i = edge[i].next ) { v = edge[i].ed; if ( leve[v] == -1 && edge[i].flow > eps ) { leve[v] = leve[u] + 1; que[rear] = v; rear = ( rear + 1 ) % MAX; } } } return leve[dest] >= 0; // leve[dest] == -1 说明没有找到增广路径 /* 有的写法是在循环中,只要到达dest立即返回true。个人感觉这样效率不会太高,因为得到的层次图是不完整的,而Dinic的优势就在于通过层次图 来优化寻找增广路的过程. */ } double dinic_dfs () { int top = 0, u, v, pos,i; double minf, res = 0; memcpy(out,head,sizeof(head)); // 将head的值拷给out stk[top++] = u = src; while ( top != 0 ) //栈空说明在当前层次图中,所有的增广路径寻找完毕 { while ( u != dest ) { for ( i = out[u]; i != -1; i = edge[i].next ) { v = edge[i].ed; if ( edge[i].flow > eps && leve[u] + 1 == leve[v] ) { out[u] = edge[i].next; // 下一次访问的时候,就可以直接得到edge[i].next pre[v] = i; // 存储边的编号 stk[top++] = v; // 栈中放的是点 u = v; break; } } if ( i == -1 ) // 若从某点出发找不到合法的边 { top--; // 把该点从栈中删除 if ( top <= 0 ) break; u = stk[top-1]; // 返回前一个点 } } if ( u == dest ) { minf = INF; while ( u != src ) { minf = min ( edge[pre[u]].flow, minf ); u = edge[pre[u]].st; } u = dest; while ( u != src ) { edge[pre[u]].flow -= minf; edge[pre[u]^1].flow += minf; if ( edge[pre[u]].flow < eps ) pos = edge[pre[u]].st; // 记录下零流出现的顶点。取最前面的哪一个。 u = edge[pre[u]].st; } while ( top > 0 && stk[top-1] != pos ) top--; // 退回到零流出现的顶点 if ( top > 0 ) u = stk[top-1]; res += minf; } } return res; } double Dinic () { double ans = 0, temp; while ( dinic_bfs() ) { temp = dinic_dfs(); if ( temp > eps ) ans+=temp; else break; } return ans; } int main() { int T, a, b, i; double val; scanf("%d",&T); while ( T-- ) { scanf("%d%d%d",&R,&C,&L); memset(head,-1,sizeof(head)); src = E = 0; dest = R + C + 1; for ( i = 1; i <= R; i++ ) { scanf("%lf",&val); add_edge ( src, i, log(val) ); } for ( i = 1; i <= C; i++ ) { scanf("%lf",&val); add_edge ( i+R, dest, log(val) ); } while ( L-- ) { scanf("%d%d",&a,&b); add_edge(a,b+R,INF); } val = Dinic(); printf("%.4f\n",exp(val)); } return 0; }
下面的代码更简洁···
#include <cmath> #include <iostream> using namespace std; #define MAX 1000 #define INF 1000000000 #define eps 0.00001 #define min(a,b) (a<b?a:b) struct Edge { int st, ed; int next; double flow; } edge[MAX*10]; int head[MAX], out[MAX]; int dep[MAX], que[MAX], stk[MAX]; int R, C, L, E; int src, dest; void add_edge ( int u, int v, double w ) { edge[E].flow = w; edge[E].st = u; edge[E].ed = v; edge[E].next = head[u]; head[u] = E++; edge[E].flow = 0; edge[E].st = v; edge[E].ed = u; edge[E].next = head[v]; head[v] = E++; } bool dinic_bfs() { memset(dep, -1, sizeof(dep)); int front = 0, rear = 0; bool flag = false; que[rear++] = src; dep[src] = 0; while ( front < rear ) { for( int i = head[que[front++]]; i != -1; i = edge[i].next ) { if ( dep[edge[i].ed] == -1 && edge[i].flow > eps ) { dep[edge[i].ed] = dep[edge[i].st] + 1; que[rear++] = edge[i].ed; if ( edge[i].ed == dest ) flag = true; // 不直接返回true效率会高些,因为构成的层次图更完整 } } } return flag; } double Dinic () { double maxFlow = 0, temp; while ( dinic_bfs() ) { int u = src, top = 0, i; for ( i = src; i <= dest; i++ ) out[i] = head[i]; while ( out[src] != -1 ) // 一个深搜的过程,当与源点关联的所有边都被访问过,则查询完毕 { if ( u == dest ) { temp = INF; for ( i = top - 1; i >= 0; i-- ) temp = min ( temp, edge[stk[i]].flow ); maxFlow += temp; for ( i = top - 1; i >= 0; i-- ) { edge[stk[i]].flow -= temp; edge[stk[i]^1].flow += temp; if ( fabs(edge[stk[i]].flow) <= eps ) top = i; // 找出零流最先出现的边,从该边的起点进行下一次增广 } u = edge[stk[top]].st; } else if ( out[u] != -1 && edge[out[u]].flow > eps && dep[u] + 1 == dep[edge[out[u]].ed] ) { stk[top++] = out[u]; // 栈中储存的是边 u = edge[out[u]].ed; } else { // 删去出度为0的边或者流量为0的边。 // 仔细体会下面的循环,当某个点的所有出边都被变了过之后,它便可以出栈了,否则out[u]应该指向下一条边 while ( top > 0 && u != src && out[u] == -1 ) u = edge[stk[--top]].st; out[u] = edge[out[u]].next; } } } return maxFlow; } int main() { int T, a, b, i; double val; scanf("%d",&T); while ( T-- ) { scanf("%d%d%d",&R,&C,&L); memset(head,-1,sizeof(head)); src = E = 0; dest = R + C + 1; for ( i = 1; i <= R; i++ ) { scanf("%lf",&val); add_edge ( src, i, log(val) ); } for ( i = 1; i <= C; i++ ) { scanf("%lf",&val); add_edge ( i+R, dest, log(val) ); } while ( L-- ) { scanf("%d%d",&a,&b); add_edge(a,b+R,INF); } val = Dinic(); printf("%.4f\n",exp(val)); } return 0; }
相关文章推荐
- POJ 3308 Paratroopers 最小点权覆盖Dinic
- POJ 3308 Paratroopers (对数转换+最小点权覆盖)
- POJ 3308 Paratroopers (对数转换+最小点权覆盖)
- POJ 3308 Paratroopers 二分图最小点权覆盖
- POJ 3308 Paratroopers(最大流最小割の最小点权覆盖)
- poj 3308 Paratroopers 最小割 最小点权覆盖
- poj 3308 Paratroopers(二分图最小点权覆盖)
- POJ 3308 Paratroopers(最小点权覆盖)(对数乘转加)
- poj 3308 Paratroopers(网络流 最小割 dinic模板)
- POJ 3308--Paratroopers【 最小点权覆盖 && 最小割】
- poj_3308 Paratroopers(二分图最小点权覆盖集+dinic+化乘为加)
- poj 3308 Paratroopers最小点权覆盖
- poj3308 Paratroopers --- 最小点权覆盖->最小割
- POJ 3308 Paratroopers(最小点权覆盖+dinic算法)
- poj3308 Paratroopers --- 最小点权覆盖->最小割
- 【POJ】3308 Paratroopers 化乘为加——最小点权覆盖
- poj 3308 Paratroopers 二分图最小点权覆盖集+ 最小割+邻接表
- POJ 3308 Paratroopers 最小点权覆盖 求最小割
- zoj 2874 & poj 3308 Paratroopers (最小割)
- POJ 3308 Paratroopers(最小割EK(邻接表&矩阵))