http://poj.org/problem?id=2455
ISAP算法:
//题目类型:最大流+二分搜索 //本题的关键在于理解题意:本题不是求最短路,而是要求路上的最长的一部分最小,故可以用二分法解决,至于路的条数,则可以求图的最大流 #include <iostream> #include <queue> //#include <conio.h> using namespace std; #define parray 40001 #define narray 201 const int INF = 10000000; typedef struct edge { int u; int v; int w; }edge; edge edges[parray]; int n,p,t; int r[narray][narray]; int d[narray]; //标号 int num[narray]; //num[i]表示标号为i的顶点数有多少 int pre[narray]; //记录前驱 int min(int a, int b) { if(a < b) return a; else return b; } void ini_d(int s,int t) //BFS计算标号,汇点t标号为0 { int k; queue<int>Q; memset(d,1,sizeof(d)); //将距离设置成为无穷大,此处亦可以使用for循环实现 memset(num,0,sizeof(num)); Q.push(t); d[t]=0; //汇点的标号为0 num[0]=1; while (!Q.empty()) { k=Q.front(),Q.pop(); for (int i=1;i<=n;i++) //遍历所有的结点 { if (d[i]>=n&&r[i][k]>0) //此处要特别注意,通过frontint的值改变其他的距离标号 { d[i]=d[k]+1; Q.push(i); num[d[i]]++; } } } } int findAlowArc(int i) //从i出发寻找允许弧 { int j; for (j=1;j<=n;j++) if (r[i][j]>0&&d[i]==d[j]+1) return j; return -1; } int reLable(int i) //重新标号 { int mm=INF; for (int j=1;j<=n;j++) if (r[i][j]>0) mm=min(mm,d[j]+1); return mm==INF?n:mm; } int maxFlow(int s,int t) //从源点s出发的最大流 { int flow=0,i=s,j; int delta; //增量 memset(pre,-1,sizeof(pre)); while (d[s]<n) { j=findAlowArc(i); if (j>=1) { pre[j]=i; i=j; //从前往后找 if (i==t) //更新残留网络 { delta=INF; for (i=t;i!=s;i=pre[i]) delta=min(delta,r[pre[i]][i]); //找到增广路径的增量 for (i=t;i!=s;i=pre[i]) r[pre[i]][i] -= delta, r[i][pre[i]] += delta; //更改流量 flow += delta; } } else { int x=reLable(i); //重新标号 num[x]++; num[d[i]]--; if (num[d[i]]==0) return flow; //间隙优化 d[i]=x; if (i!=s) i=pre[i]; } } return flow; } void build(int length) { int i,j; memset(r,0,sizeof(r)); for(i=1;i<=p;++i) { if(edges[i].w<=length) { r[edges[i].u][edges[i].v]++; r[edges[i].v][edges[i].u]++; } } } int main() { // freopen("in.txt","r",stdin); int i,j; int src,des; while(scanf("%d%d%d",&n,&p,&t)!=-1) { for(i=1;i<=p;++i) { scanf("%d%d%d",&edges[i].u,&edges[i].v,&edges[i].w); } src = 1; des = n; int l = 0,h = 1000000,mid; while(l<h) { mid = (l+h)/2; build(mid); ini_d(src,des); if(maxFlow(src,des)>=t) h=mid; else l = mid+1; } printf("%d/n",h); } //getch(); return 0; }
EK算法:
//二分答案+最大流 //终于见识到传说中的二分答案这个方法了 //如果没有二分答案这个提示,这道题我怎么也不会往最大流去想 //思路是这样的,将所有边权值排序,记录边权值的上界和下界 //接着开始二分答案,依照枚举的这个答案构图,即将边权值最大值小于等于枚举的这个答案的边加进图中 //(双向边)并将他们的容量设为1,有多重边就将容量再+1,接着FF最大流,如果最大流>=T,则说明答案是满足条件的 //记住此时未必是正确答案,答案还可以再小。继续二分,知道最终答案出来 //二分后最终答案是下界,即程序中的l,而不是mid中间值!记得~~因为这个WA了几次 #include<iostream> #include<queue> #include<vector> using namespace std; const int MAXN = 205,MAXM = 40005; const int INF = 2147483647; struct Edge { int u,v,w; Edge(int uu,int vv,int ww) { u = uu; v = vv; w = ww; } }; int rflow[MAXN],flow[MAXN][MAXN],cap[MAXN][MAXN],pre[MAXN]; //cap[i][j]表示i与j之间的容量,flow[i][j]表示i和j之间的流量,Pre[i]表示i的前一个节点 //rflow[i]表示残留量 int l,r,mid,maxflow; int N,T,P; vector<Edge> E; int min(int a, int b) { if(a < b) return a; else return b; } void buildGraph(int ans) { memset(cap,0,sizeof(cap)); for(int i = 0;i < E.size();++i) { if(E[i].w <= ans) { cap[E[i].u][E[i].v] += 1; cap[E[i].v][E[i].u] += 1; } } } void ek()//FF模板 { queue<int> q; int u, v; memset(flow,0,sizeof(flow)); maxflow = 0; while(1) { memset(rflow,0,sizeof(rflow)); rflow[1] = INF; q.push(1); while(!q.empty()) { u = q.front(); q.pop(); for(v = 1;v <= N;++v) if(rflow[v] == 0 && cap[u][v] > flow[u][v]) { pre[v] = u; q.push(v); rflow[v] = min(rflow[u],cap[u][v] - flow[u][v]); } } if(rflow[N] == 0) break; for(u = N;u != 1;u = pre[u]) { flow[pre[u]][u] += rflow[N]; flow[u][pre[u]] -= rflow[N]; } maxflow += rflow[N]; } } int main() { // freopen("in.txt","r",stdin); int u,v,w; scanf("%d%d%d",&N,&P,&T); l = INF; r = -INF; while(P--) { scanf("%d%d%d",&u,&v,&w); if(w < l) l = w; if(w > r) r = w; E.push_back(Edge(u,v,w)); } while(l <= r) { mid = (r+l) / 2; buildGraph(mid); ek(); if(maxflow >= T) r = mid - 1; else l = mid + 1; } printf("%d/n",l); return 0; }