poj2455 Secret Milking Machine 网络流 最大流 二份答案
来源:互联网 发布:2016chinajoy数据 编辑:程序博客网 时间:2024/05/19 03:27
Description
The farm comprises N (2 <= N <= 200) landmarks (numbered 1..N) connected by P (1 <= P <= 40,000) bidirectional trails (numbered 1..P) and with a positive length that does not exceed 1,000,000. Multiple trails might join a pair of landmarks.
To minimize his chances of detection, FJ knows he cannot use any trail on the farm more than once and that he should try to use the shortest trails.
Help FJ get from the barn (landmark 1) to the secret milking machine (landmark N) a total of T times. Find the minimum possible length of the longest single trail that he will have to use, subject to the constraint that he use no trail more than once. (Note well: The goal is to minimize the length of the longest trail, not the sum of the trail lengths.)
It is guaranteed that FJ can make all T trips without reusing a trail.
Input
* Lines 2..P+1: Line i+1 contains three space-separated integers, A_i, B_i, and L_i, indicating that a trail connects landmark A_i to landmark B_i with length L_i.
Output
Sample Input
7 9 21 2 22 3 53 7 51 4 14 3 14 5 75 7 11 6 36 7 3
Sample Output
5
Hint
Huge input data,scanf is recommended.
Source
为什么网络流的题目这么多英文啊。。。。看题目看的痛苦啊!
google翻译+修正
农民约翰正在建设一个新的挤奶机且希望保持尽可能长的秘密。他隐藏在深处他的农场,需要能够得到而不被发现的机。他必须做一个总的T(1 <= T <= 200)行程去施工过程中的机器。他有一个秘密的隧道,他只使用回程。
农场包括N(2<=N <= 200)地标(编号为1..N)连接由P(1<= P<= 40,000)双向步道(编号为1.. P),并正向的长度不超过1,000,000。多径可能会加入一地标。
为了尽量减少检测的机会,FJ知道他不能在农场使用任何线索,不止一次地和他应该尽量使用最短的步道。
帮助FJ从谷仓(地标1)到秘密挤奶机(地标N)的T次。寻找最小可能长度最长的单线索,他将不得不使用,受到约束,他不止一次地使用无踪迹。(请注意:我们的目标是尽量减少长度最长的步道,步道长度的总和。)
这是保证,FJ可以不重用一条小道所有T人次。
简单的说就是:
给出无向图,问从点1到点N的T条不重复路径的最大权边的权最小是多少。数据保证存在T条不重复路径。
二分枚举最大边权,重新建图,只保存权不超过最大边权的边。然后在网络中2点间的容量是原图中该2点间的边数,判断最大流是否>=T
网上的说ek算法会超时。我自己敲了一遍貌似没有超时啊 rp不错啊
思路是这样的,记录所有边权值的上界和下界,接着开始二分答案(按边权值进行二分搜索),依照枚举的这个答案构图,即将边权值最大值小于等于枚举的这个答案的边加进图中 。(双向边)并将他们的容量设为1,有多重边就将容量再+1,(因为题目要求节点次数大于T,所以多重边的容量+1,就相当于网络流来搜索流量大于T的),接着求最大流,如果最大流>=T,则说明答案是满足条件的。,接着继续搜比现在更小的答案,直至最终答案出来。
网上还有用vector写的。速度更快。我是自己建边集,但是也可以过。
#include<iostream>#define parray 40004#define narray 204#define INF 10000000using namespace std;int c[narray][narray];int edge[parray][3];int n,p,t;int l,r,mid;int con;void buildgraph(int ans){memset(c,0,sizeof(c));for(int i=0;i<con;++i){if(edge[i][2]<=ans){c[edge[i][0]][edge[i][1]]+=1;c[edge[i][1]][edge[i][0]]+=1;}}}int ek( int s, int t ) // 输入源点和汇点{ int p, q, queue[parray], u, v, pre[narray], flow= 0, aug;while(true){ memset(pre,-1,sizeof(pre)); //记录父节点for( queue[p=q=0]=s; p<=q; p++ ) //广度优先搜索 p是头指针,q是尾指针 初始化皆为0 {u= queue[p];//原点是s 汇点是t n是顶点数 for( v=1; v<=n&&pre[t]<0; v++ )if( c[u][v]>0 && pre[v]<0 ) //如果两者u到v连通 且v的父节点没有记录 则记录v的父节点是u,且入队 pre[v]=u, queue[++q]=v; //找完 汇点的父节点已经找到了 if( pre[t]>=0 ) break;}//上面bfs是找增广路的 if( pre[t]<0 ) break; //不存在增广路(while退出条件,直至没有增广路就找到最大流了) aug= 0x7fffffff; //记录最小残留容量 for( u=pre[v=t]; v!=s; v=u,u=pre[u] )//u赋值给v u=u的父亲节点,那么u,v就是一个连通量 if(c[u][v]<aug) aug=c[u][v]; //找最小的残留量 for( u=pre[v=t]; v!=s; v=u,u=pre[u] ) //更新记录流量 c[u][v]-=aug, c[v][u]+=aug; //u到v是正流量用-= v到u是反流量用+= flow+= aug; } return flow; } int main(){int u,v,w;scanf("%d%d%d",&n,&p,&t);l=INF;r=-INF;con=0;memset(edge,0,sizeof(edge));while(p--){scanf("%d%d%d",&u,&v,&w);if(w<l)l=w;if(w>r)r=w;edge[con][0]=u;edge[con][1]=v;edge[con][2]=w;con++;}while(l<=r){mid=(r+l)/2;buildgraph(mid);if(ek(1,n)>=t)r=mid-1;elsel=mid+1;}printf("%d\n",l);return 0;}
网上还有sap的做法,我也转载了过来仅供参考:
//题目类型:最大流+二分搜索 //本题的关键在于理解题意:本题不是求最短路,而是要求路上的最长的一部分最小,故可以用二分法解决,至于路的条数,则可以求图的最大流 #include <iostream>#include <queue>//#include <conio.h>using namespace std;#define parray 40001#define narray 201const 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;elsereturn 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的用vector写的。
#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;elsereturn 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;}
- poj2455 Secret Milking Machine 网络流 最大流 二份答案
- POJ2455 Secret Milking Machine 典型二分答案+最大流判定
- poj2455 Secret Milking Machine(二分答案+最大流)
- POJ2455 Secret Milking Machine ——二分答案+网络流
- POJ2455 Secret Milking Machine
- POJ2455-Secret Milking Machine
- POJ2455 Secret Milking Machine(二分上线构图求最大流)
- POJ 2455 Secret Milking Machine(二分答案+最大流)
- POJ 2455 Secret Milking Machine 最大流 二分答案
- POJ 2455 Secret Milking Machine (二分答案+最大流)
- POJ 2455 Secret Milking Machine (最大流)
- 最大流+二分Secret Milking Machine
- 解题报告 之 POJ2455 Secret Milking Machine
- POJ Secret Milking Machine 【网络流+二分】
- 【网络流】 POJ 2455 Secret Milking Machine
- POJ 2455Secret Milking Machine(二分+网络流之最大流)
- POJ 2455 Secret Milking Machine(搜索-二分,网络流-最大流)
- POJ 2455-Secret Milking Machine(网络流_最大流+二分查找)
- [Android实例] Android activity动画跳转案例分析
- 各种音视频编解码学习详解 h264 ,mpeg4 ,aac 等所有音视频格式
- linux 线程安全 mutex 锁的使用
- Android 动画
- ------------------------MSSQL中排列组合的实现------------------------------------
- poj2455 Secret Milking Machine 网络流 最大流 二份答案
- HDU-1242 Rescue
- android_launcher的源码详细分析
- Android动画入门
- win8开发应用之二:管理应用生命周期和状态(使用 C# 和 XAML 的 Metro 风格应用)
- IOS开发之百度地图API应用
- 关于java和php的sign加密问题
- Objective-C研究院之数组对象(七)
- DictionarySync和ListSync 线程安全类