POJ 2455 网络流 基础题 二分+网络流 dicnic 以及 sap算法

Secret Milking Machine
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 8189   Accepted: 2485

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 2

1 2 2

2 3 5

3 7 5

1 4 1

4 3 1

4 5 7

5 7 1

1 6 3

6 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


题意:

题意:FJ有N块地,这些地之间有P条双向路,每条路的都有固定的长度l。现在要你找出从第1块地到第n块地的T条不同路径,每条路径上的路不能与先前的路径重复,问这些路径中的最长路的最小是多少。

 

思路:二分答案+网络流判定。
二分枚举最大边权,重新建图,只保存权不超过最大边权的边。即如果边的长度小于等于我们规定的最大边权 则添加这条边 权值为1, 否则标记为0 
然后在网络中起点终点间的容量是原图中的路径数,判断最大流是否>=T
 

只需要对模板就行修改下 之后二分即可   只需要修改添加边的地方  因为本题是双向边 所以添加的边以及其反方向边 都应该权值为cw


#include <stdio.h>

#include <string.h>

#define VM 222

#define EM 81111*2

#define inf 0x3f3f3f3f

struct Edge

{

    int frm,to,cap,next;

}edge[EM];



int head[VM],dep[VM],ep,n;     //dep为点的层次

void addedge (int cu,int cv,int cw)  //第一条边下标必须为偶数

{

    edge[ep].frm = cu;

    edge[ep].to = cv;

    edge[ep].cap = cw;

    edge[ep].next = head[cu];

    head[cu] = ep;

    ep ++;

    edge[ep].frm = cv;

    edge[ep].to = cu;

    edge[ep].cap = cw;

    edge[ep].next = head[cv];

    head[cv] = ep;

    ep ++;

}



int BFS (int src,int des)     //求出层次图

{

    int que[VM],i,front = 0,rear = 0;

    memset (dep,-1,sizeof(dep));

    que[rear++] = src;

    dep[src] = 0;

    while (front != rear)

    {

        int u = que[front++];

        front = front%VM;

        for (i = head[u];i != -1;i = edge[i].next)

        {

            int v = edge[i].to;

            if (edge[i].cap > 0&&dep[v] == -1) //容量大于0&&未在dep中

            {

                dep[v] = dep[u] + 1;        //建立层次图

                que[rear ++] = v;

                rear = rear % VM;

                if (v == des)  //找到汇点 返回

                    return 1;

            }

        }

    }

    return 0;

}

int dinic (int src,int des)

{

    int i,res = 0,top;

    int stack[VM];    //stack为栈,存储当前增广路

    int cur[VM];        //存储当前点的后继 跟head是一样的

    while (BFS(src,des))   //if BFS找到增广路

    {

        memcpy (cur,head,sizeof (head));

        int u = src;       //u为当前结点

        top = 0;

        while (1)

        {

            if (u == des)     //增广路已全部进栈

            {

                int min = inf,loc ;

                for (i = 0;i < top;i ++)       //找最小的增广跟并loc记录其在stack中位置

                    if (min > edge[stack[i]].cap)  //以便退回该边继续DFS

                    {

                        min = edge[stack[i]].cap;

                        loc = i;

                    }

                for (i = 0;i < top;i ++)   //偶数^1 相当加1 奇数^1相当减1 当正向边 = 0&&路径不合适时,正加负减

                {                           //偶数是正向边,奇数是负向边,边从0开始

                    edge[stack[i]].cap -= min;

                    edge[stack[i]^1].cap += min;

                }                              //将增广路中的所有边修改

                res += min;

                top = loc;

                u = edge[stack[top]].frm;         //当前结点修改为最小边的起点

            }

            for (i = cur[u];i != -1;cur[u] = i = edge[i].next)   //找到当前结点对应的下一条边

                if (edge[i].cap != 0&&dep[u] + 1 == dep[edge[i].to])//不满足条件时,修改cur值(去掉不合适的占)eg:1-->2 1-->3 1-->4 有边 但只有

                    break;                                  // 1-->4 这条边满足条件 就把1到2、3的边给去掉

            if (cur[u] != -1)            //当前结点的下一条边存在

            {

                stack[top ++] = cur[u];   //把该边放入栈中

                u = edge[cur[u]].to;         //再从下个点开始找

            }

            else

            {

                if (top == 0)        //当前结点无未遍历的下一条边且栈空,DFS找不到下一条增广路

                    break;

                dep[u] = -1;            //当前结点不在增广路中,剔除该点

                u = edge[stack[--top]].frm; //退栈 回朔,继续查找

            }

        }

    }

    return res;

}

struct IN

{

    int x,y,c;

}q[EM];

int sovle(int mid,int src,int des,int m,int k)

{

    ep = 0;

    memset (head,-1,sizeof(head));

    for(int i=0;i<m;i++)

    {

       if(q[i].c<=mid)

        addedge(q[i].x,q[i].y,1);

       // addedge(q[i].y,q[i].x,1);



    }

     if(dinic (src,des)>=k) return 1;

     return 0;

}

int main ()

{

    int n,m,v1,v2,w,k,i;

    int src,des;

    while (scanf ("%d %d %d",&n,&m,&k)!=EOF)

    {

       // ep = 0;

        src=1;des=n;

       // memset (head,-1,sizeof(head));

        for(i=0;i<m;i++)

        {

            scanf("%d %d %d",&v1,&v2,&w);

            q[i].x=v1;q[i].y=v2;q[i].c=w;

        }

        int left,right,mid,ans=-1;

        left=0;right=1000000;

        while(left<=right)

        {

            mid=(left+right)/2;

            if(sovle(mid,src,des,m,k))

            {

                ans=mid;

                right=mid-1;

            }

            else left=mid+1;

        }

        printf ("%d\n",ans);

    }

    return 0;

}




Sap 算法

#include <stdio.h>

#include <string.h>

#define VM 222

#define EM 81111*2

#define inf 0x3f3f3f3f





struct Edge

{

    int to, frm, nxt, cap;

}edge[EM];



int head[VM], ep, n, src, des;

int dep[VM], gap[VM]; //gap[x]=y:说明残留网络中dep[i]=x的个数为y



void addedge(int u, int v, int c)

{

    edge[ep].frm = u;

    edge[ep].to = v;

    edge[ep].cap = c;

    edge[ep].nxt = head[u];

    head[u] = ep++;

    edge[ep].frm = v;

    edge[ep].to = u;

    edge[ep].cap = 0;

    edge[ep].nxt = head[v];

    head[v] = ep++;

}



void BFS()

{

    memset(dep, -1, sizeof(dep));

    memset(gap, 0, sizeof(gap));

    gap[0] = 1;   //说明此时有1个dep[i] = 0

    int que[VM], front = 0, rear = 0;

    dep[des] = 0;

    que[rear++] = des;

    int u, v;

    while (front != rear)

    {

        u = que[front++];

        front = front%VM;

        for (int i=head[u]; i!=-1; i=edge[i].nxt)

        {

            v = edge[i].to;

            if (edge[i].cap != 0 || dep[v] != -1)

                continue;

            que[rear++] = v;

            rear = rear % VM;

            ++gap[dep[v] = dep[u] + 1];  //求出各层次的数量

        }

    }

}



int Sap()

{

    int res = 0;

    BFS();

    int cur[VM];

    int stack[VM], top = 0;

    memcpy(cur, head, sizeof(head));

    int u = src, i;

    while (dep[src] < n)

    {

        if (u == des)

        {

            int temp = inf, inser = n;

            for (i=0; i!=top; ++i)

                if (temp > edge[stack[i]].cap)

                {

                    temp = edge[stack[i]].cap;

                    inser = i;

                }

            for (i=0; i!=top; ++i)

            {

                edge[stack[i]].cap -= temp;

                edge[stack[i]^1].cap += temp;

            }

            res += temp;

            top = inser;

            u = edge[stack[top]].frm;

        }



        if (dep[u] != 0 && gap[dep[u] -1] == 0)//出现断层,无增广路

            break;

        for (i = cur[u]; i != -1; i = edge[i].nxt)//遍历与u相连的未遍历结点

            if (dep[edge[i].to] != -1)

                if (edge[i].cap != 0 && dep[u] == dep[edge[i].to] + 1) //层序关系, 找到允许

                    break;



        if (i != -1)//找到允许弧

        {

            cur[u] = i;

            stack[top++] = i;//加入路径栈

            u = edge[i].to;//查找下一个结点

        }

        else   //无允许的路径,修改标号 当前点的标号比与之相连的点中最小的多1

        {

            int min = n;

            for (i = head[u]; i != -1; i = edge[i].nxt) //找到与u相连的v中dep[v]最小的点

            {

                if (edge[i].cap == 0)

                    continue;

                if (min > dep[edge[i].to])

                {

                    min = dep[edge[i].to];

                    cur[u] = i;          //最小标号就是最新的允许弧

                }

            }

            --gap[dep[u]];          //dep[u] 的个数变化了 所以修改gap

            ++gap[dep[u] = min + 1]; //将dep[u]设为min(dep[v]) + 1, 同时修改相应的gap[]

            if (u != src) //该点非源点&&以u开始的允许弧不存在,退点

                u = edge[stack[--top]].frm;

        }

    }

    return res;

}









struct IN

{

    int x,y,c;

}q[EM];

int sovle(int mid,int m,int k)

{

    //printf("jin\n");

    ep = 0;

    memset (head,-1,sizeof(head));

    for(int i=0;i<m;i++)

    {

       if(q[i].c<=mid)

         {

             addedge(q[i].x,q[i].y,1);

             addedge(q[i].y,q[i].x,1);//不知道为什么 sap需要添加2次 相反边  

         }

    }

     if(Sap()>=k) return 1;

     return 0;

}

int main ()

{

    int m,v1,v2,w,k,i;

    while (scanf ("%d %d %d",&n,&m,&k)!=EOF)

    {

        src=1;des=n;

        for(i=0;i<m;i++)

        {

            scanf("%d %d %d",&v1,&v2,&w);

            q[i].x=v1;q[i].y=v2;q[i].c=w;

        }

        int left,right,mid,ans=-1;

        left=0;right=1000000;

        while(left<=right)

        {

            mid=(left+right)/2;

            if(sovle(mid,m,k))

            {

                ans=mid;

                right=mid-1;

            }

            else left=mid+1;

        }

        printf ("%d\n",ans);

    }

    return 0;

}








 

你可能感兴趣的:(poj)