题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4280
题目大意:
有N个岛屿,M条双向道路。每条路每小时最多能通过Ci个人。给你N个岛屿的坐标。问:一个小时内,
最多能将多少游客从最西边的岛送至最东边的岛屿上。
思路:
网络流求最大流的裸题。先通过坐标找到最西边的岛屿和最东边的岛屿,记录并标记为源点和汇点。然后
用链式前向星来存储图,将M条双向边加入到图中。然后用SAP算法来做,据说还没有卡SAP的网络流。
算法用了GAP优化、当前弧优化,具体参考代码。
AC代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> using namespace std; const int MAXN = 100010; //最大点个数 const int MAXM = MAXN*4; const int INF = 0xffffff0; struct EdgeNode { int to; int next; int w; }Edges[MAXM]; int Head[MAXN],id; //链式前向星 void AddEdges(int u,int v,int w) { Edges[id].to = v; Edges[id].w = w; Edges[id].next = Head[u]; Head[u] = id++; Edges[id].to = u; Edges[id].w = 0; Edges[id].next = Head[v]; Head[v] = id++; } int Numh[MAXN],h[MAXN],curedges[MAXN],pre[MAXN]; //Numh:用于GAP优化的统计高度数量数组; //h:距离标号数组; //curedges:当前弧数组 //pre:前驱数组 void BFS(int end,int N) //BFS求出每个点的距离标号值 { memset(Numh,0,sizeof(Numh)); for(int i = 1; i <= N; ++i) Numh[h[i]=N]++; h[end] = 0; Numh[N]--; Numh[0]++; queue<int> Q; Q.push(end); while(!Q.empty()) { int v = Q.front(); Q.pop(); int i = Head[v]; while(i != -1) { int u = Edges[i].to; if(h[u] < N) { i = Edges[i].next; continue; } h[u] = h[v] + 1; Numh[N]--; Numh[h[u]]++; Q.push(u); i = Edges[i].next; } } } int SAPMaxFlow(int start,int end,int N) { int CurFlow,FlowAns = 0,temp,neck; //FlowAns:最大流,初始化为0 memset(h,0,sizeof(h)); memset(pre,-1,sizeof(pre)); for(int i = 1; i <= N; ++i) curedges[i] = Head[i]; //初始化当前弧为第一条邻接边 //Numh[0] = N; BFS(end,N); int u = start; while(h[start] < N) //当h[start] >= N时,网络中肯定出现了GAP { if(u == end) { CurFlow = INF; for(int i = start; i != end; i = Edges[curedges[i]].to) { if(CurFlow > Edges[curedges[i]].w) { neck = i; CurFlow = Edges[curedges[i]].w; } } //增广成功,寻找"瓶颈"边 for(int i = start; i != end; i = Edges[curedges[i]].to) { temp = curedges[i]; Edges[temp].w -= CurFlow; Edges[temp^1].w += CurFlow; } //修改路径上的边容量 FlowAns += CurFlow; u = neck; //下一次增广从瓶颈边开始 } int i; for(i = curedges[u]; i != -1; i = Edges[i].next) if(Edges[i].w && h[u] == h[Edges[i].to]+1) break; //寻找可行弧 if(i != -1) //GAP优化 { curedges[u] = i; pre[Edges[i].to] = u; u = Edges[i].to; } else { if(0 == --Numh[h[u]]) break; curedges[u] = Head[u]; for(temp = N,i = Head[u]; i != -1; i = Edges[i].next) if(Edges[i].w) temp = min(temp,h[Edges[i].to]); h[u] = temp + 1; ++Numh[h[u]]; if(u != start) //重新标号并从当前点前驱重新增广 u = pre[u]; } } return FlowAns; } int main() { int T,N,M,u,v,w; int start,end; scanf("%d",&T); while(T--) { int Max = -INF,Min = INF; scanf("%d%d",&N,&M); for(int i = 1; i <= N; ++i) //找到最西边和最东边的岛,记录源点和汇点 { scanf("%d%d",&u,&v); if(u <= Min) { Min = u; start = i; } if(u >= Max) { Max = u; end = i; } } memset(Head,-1,sizeof(Head)); id = 0; for(int i = 0; i < M; ++i) //添加边 { scanf("%d%d%d",&u,&v,&w); AddEdges(u,v,w); AddEdges(v,u,w); } printf("%d\n",SAPMaxFlow(start,end,N)); } return 0; }
时间: 2024-11-05 16:54:03