poj2455 Secret Milking Machine 网络流 最大流 二份答案

来源:互联网 发布:2016chinajoy数据 编辑:程序博客网 时间:2024/05/19 03:27
Secret Milking Machine
Time Limit: 1000MS Memory Limit: 65536KTotal Submissions: 6595 Accepted: 2043

Description

Farmer John is constructing a new milking machine and wishes to keep it secret as long as possible. He has hidden in it deep within his farm and needs to be able to get to the machine without being detected. He must make a total of T (1 <= T <= 200) trips to the machine during its construction. He has a secret tunnel that he uses only for the return trips.
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

* Line 1: Three space-separated integers: N, P, and T
* 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

* Line 1: A single integer that is the minimum possible length of the longest segment of Farmer John's route.

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

Farmer John can travel trails 1 - 2 - 3 - 7 and 1 - 6 - 7. None of the trails travelled exceeds 5 units in length. It is impossible for Farmer John to travel from 1 to 7 twice without using at least one trail of length 5.

Huge input data,scanf is recommended.

Source

USACO 2005 February Gold

为什么网络流的题目这么多英文啊。。。。看题目看的痛苦啊!

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;}