HDU 4280 Island Transport (Dinic非递归)

Island Transport

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2822    Accepted Submission(s): 922


Problem Description
  In the vast waters far far away, there are many islands. People are living on the islands, and all the transport among the islands relies on the ships.
  You have a transportation company there. Some routes are opened for passengers. Each route is a straight line connecting two different islands, and it is bidirectional. Within an hour, a route can transport a certain number of passengers in one direction. For safety, no two routes are cross or overlap and no routes will pass an island except the departing island and the arriving island. Each island can be treated as a point on the XY plane coordinate system. X coordinate increase from west to east, and Y coordinate increase from south to north.
  The transport capacity is important to you. Suppose many passengers depart from the westernmost island and would like to arrive at the easternmost island, the maximum number of passengers arrive at the latter within every hour is the transport capacity. Please calculate it.
 
Input
  The first line contains one integer T (1<=T<=20), the number of test cases.
  Then T test cases follow. The first line of each test case contains two integers N and M (2<=N,M<=100000), the number of islands and the number of routes. Islands are number from 1 to N.
  Then N lines follow. Each line contain two integers, the X and Y coordinate of an island. The K-th line in the N lines describes the island K. The absolute values of all the coordinates are no more than 100000.
  Then M lines follow. Each line contains three integers I1, I2 (1<=I1,I2<=N) and C (1<=C<=10000) . It means there is a route connecting island I1 and island I2, and it can transport C passengers in one direction within an hour.
  It is guaranteed that the routes obey the rules described above. There is only one island is westernmost and only one island is easternmost. No two islands would have the same coordinates. Each island can go to any other island by the routes.
 
Output
  For each test case, output an integer in one line, the transport capacity.
 
Sample Input
2 5 7 3 3 3 0 3 1 0 0 4 5 1 3 3 2 3 4 2 4 3 1 5 6 4 5 3 1 4 4 3 4 2 6 7 -1 -1 0 1 0 2 1 0 1 1 2 3 1 2 1 2 3 6 4 5 5 5 6 3 1 4 6 2 5 5 3 6 4
 
Sample Output
9 6
 
Source
 
Recommend
liuyiding
 

   题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4280

     题意:有N个岛屿之间有M双向条路,每条路每个小时最多能通过C个人,现在问一个小时内,最多能把多少个顾客从最西边的岛屿送至最东边的岛屿上。

     思路:网络流,求最大流。建图:每条路连接的两个岛屿之间建立一条容量为C的双向边,取超级源点与汇点,源点与最西边的岛屿,汇点与最东边的岛屿建立一条流量为无穷大的边。

      考点:网络流,Dinic非递归版,如果使用递归版的Dinic,将RE!

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

const int VM=100010;
const int EM=100010;
const int INF=0x3f3f3f3f;

struct Edge{
    int u,v,nxt;
    int cap;
}edge[EM<<1];

int n,m,cnt,head[VM],src,des;
int dep[VM],que[VM];

void addedge(int cu,int cv,int cw){
    edge[cnt].u=cu;     edge[cnt].v=cv;     edge[cnt].cap=cw;
    edge[cnt].nxt=head[cu];     head[cu]=cnt++;
    edge[cnt].u=cv;     edge[cnt].v=cu;     edge[cnt].cap=cw;
    edge[cnt].nxt=head[cv];     head[cv]=cnt++;
}

int BFS(){
    int front=0,rear=0;
    memset(dep,-1,sizeof(dep));
    que[rear++]=src;
    dep[src]=0;
    while(front!=rear){
        int u=que[front++];
        front%=VM;
        for(int i=head[u];i!=-1;i=edge[i].nxt){
            int v=edge[i].v;
            if(edge[i].cap>0 && dep[v]==-1){
                dep[v]=dep[u]+1;
                que[rear++]=v;
                rear%=VM;
                if(v==des)      //优化1
                    return 1;
            }
        }
    }
    return 0;
}


int Dinic(){
    int ans=0;
    int stack[VM],cur[VM]; ////stack[i为栈,存储当前增广路,  cur[i]存储当前点的后继 跟head是一样的
    while(BFS()){
        memcpy(cur,head,sizeof(head));
        int u=src, top=0;   //u为当前结点
        while(1){
            if(u==des){     //增广路已全部进栈
                int minx=INF, loc;
                for(int i=0;i<top;i++)  //找最小的增广跟并loc记录其在stack中位置
                    if(minx>edge[stack[i]].cap){    //以便退回该边继续DFS
                        minx=edge[stack[i]].cap;
                        loc=i;
                    }
                for(int i=0;i<top;i++){     //将增广路中的所有边修改
                    edge[stack[i]].cap-=minx;
                    edge[stack[i]^1].cap+=minx;
                }
                ans+=minx;
                top=loc;
                u=edge[stack[top]].u;   //当前结点修改为最小边的起点
            }
            for(int i=cur[u];i!=-1;cur[u]=i=edge[i].nxt)  //找到当前结点对应的下一条边
                if(edge[i].cap>0 && dep[edge[i].v]==dep[u]+1)   //不满足条件时,修改cur值
                    break;  //(去掉不合适的占)eg:1-->2 1-->3 1-->4 有边 但只有1-->4 这条边满足条件 就把1到2、3的边给去掉
            if(cur[u]!=-1){    //当前结点的下一条边存在
                stack[top++]=cur[u];   //把该边放入栈中
                u=edge[cur[u]].v;      //再从下个点开始找
            }else{
                if(top==0)  //当前结点无未遍历的下一条边且栈空,DFS找不到下一条增广路
                    break;
                dep[u]=-1;  //当前结点不在增广路中,剔除该点
                u=edge[stack[--top]].u;     //退栈 回朔,继续查找
            }
        }
    }
    return ans;
}

int main(){

    //freopen("input.txt","r",stdin);

    int t;
    scanf("%d",&t);
    while(t--){
        cnt=0;
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);
        int sx=INF,sy=INF,tx=-INF,ty=-INF,x,y,s,t;
        for(int i=1;i<=n;i++){
            scanf("%d%d",&x,&y);
            if(sx>x){
                sx=x;sy=y;
                s=i;
            }else if(sx==x && sy>y){
                sy=y;
                s=i;
            }
            if(tx<x){
                tx=x;ty=y;
                t=i;
            }else if(tx==x && ty<y){
                ty=y;
                t=i;
            }
        }
        src=0,des=n+1;
        addedge(src,s,INF);
        addedge(t,des,INF);
        int u,v,w;
        while(m--){
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
        }
        printf("%d\n",Dinic());
    }
    return 0;
}

用SAP竟然TLE了,纳闷。。。。。。。

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

const int VM=100010;
const int EM=100010;
const int INF=0x3f3f3f3f;

struct Edge{
    int to,nxt;
    int cap;
}edge[EM<<1];

int n,m,cnt,head[VM];
int dep[VM],gap[VM],cur[VM],aug[VM],pre[VM];

void addedge(int cu,int cv,int cw){
    edge[cnt].to=cv;  edge[cnt].cap=cw;  edge[cnt].nxt=head[cu];
    head[cu]=cnt++;
    edge[cnt].to=cu;  edge[cnt].cap=cw;   edge[cnt].nxt=head[cv];
    head[cv]=cnt++;
}

int src,des;

int SAP(int n){
    int max_flow=0,u=src,v;
    int id,mindep;
    aug[src]=INF;
    pre[src]=-1;
    memset(dep,0,sizeof(dep));
    memset(gap,0,sizeof(gap));
    gap[0]=n;
    for(int i=0;i<=n;i++)
        cur[i]=head[i]; // 初始化当前弧为第一条弧
    while(dep[src]<n){
        int flag=0;
        if(u==des){
            max_flow+=aug[des];
            for(v=pre[des];v!=-1;v=pre[v]){     // 路径回溯更新残留网络
                id=cur[v];
                edge[id].cap-=aug[des];
                edge[id^1].cap+=aug[des];
                aug[v]-=aug[des];   // 修改可增广量,以后会用到
                if(edge[id].cap==0) // 不回退到源点,仅回退到容量为0的弧的弧尾
                    u=v;
            }
        }
        for(int i=cur[u];i!=-1;i=edge[i].nxt){
            v=edge[i].to;    // 从当前弧开始查找允许弧
            if(edge[i].cap>0 && dep[u]==dep[v]+1){  // 找到允许弧
                flag=1;
                pre[v]=u;
                cur[u]=i;
                aug[v]=min(aug[u],edge[i].cap);
                u=v;
                break;
            }
        }
        if(!flag){
            if(--gap[dep[u]]==0)    /* gap优化,层次树出现断层则结束算法 */
                break;
            mindep=n;
            cur[u]=head[u];
            for(int i=head[u];i!=-1;i=edge[i].nxt){
                v=edge[i].to;
                if(edge[i].cap>0 && dep[v]<mindep){
                    mindep=dep[v];
                    cur[u]=i;   // 修改标号的同时修改当前弧
                }
            }
            dep[u]=mindep+1;
            gap[dep[u]]++;
            if(u!=src)  // 回溯继续寻找允许弧
                u=pre[u];
        }
    }
    return max_flow;
}

int main(){

    //freopen("input.txt","r",stdin);

    int t;
    scanf("%d",&t);
    while(t--){
        cnt=0;
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);
        int sx=INF,sy=INF,tx=-INF,ty=-INF,x,y,s,t;
        for(int i=1;i<=n;i++){
            scanf("%d%d",&x,&y);
            if(sx>x){
                sx=x;sy=y;
                s=i;
            }else if(sx==x && sy>y){
                sy=y;
                s=i;
            }
            if(tx<x){
                tx=x;ty=y;
                t=i;
            }else if(tx==x && ty<y){
                ty=y;
                t=i;
            }
        }
        src=0,des=n+1;
        addedge(src,s,INF);
        addedge(t,des,INF);
        int u,v,w;
        while(m--){
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
        }
        printf("%d\n",SAP(des+1));
    }
    return 0;
}
View Code

 

某位大神加了句代码,用递归地dinic也搞定:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<queue>
  4 #include<cmath>
  5 //就是这句了,真心亮啊!!!
  6 #pragma comment(linker, "/STACK:1024000000,1024000000")
  7 
  8 #define find_min(a,b) a<b?a:b
  9 using namespace std;
 10 
 11 const int N = 100010;
 12 const int MAX = 10000000;
 13 
 14 struct Edge{
 15     int s,e,next;
 16     int v;
 17 }edge[2*N];
 18 
 19 int n,e_num,head[N],d[N],sp,tp;
 20 
 21 void AddEdge(int a,int b,int c){
 22     edge[e_num].s=a; edge[e_num].e=b; edge[e_num].v=c;
 23     edge[e_num].next=head[a]; head[a]=e_num++;
 24 
 25     edge[e_num].s=b; edge[e_num].e=a; edge[e_num].v=c;
 26     edge[e_num].next=head[b]; head[b]=e_num++;
 27 }
 28 
 29 int bfs(){
 30     queue <int> q;
 31     memset(d,-1,sizeof(d));
 32     d[sp]=0;
 33     q.push(sp);
 34     while(!q.empty()){
 35         int cur=q.front();
 36         q.pop();
 37         for(int i=head[cur];i!=-1;i=edge[i].next){
 38             int u=edge[i].e;
 39             if(d[u]==-1 && edge[i].v>0){//没有标记,且可行流大于0
 40                 d[u]=d[cur]+1;
 41                 q.push(u);
 42             }
 43         }
 44     }
 45     return d[tp] != -1;//汇点是否成功标号,也就是说是否找到增广路
 46 }
 47 
 48 int dfs(int a,int b){//a为起点
 49     double r=0;
 50     if(a==tp)return b;
 51     for(int i=head[a];i!=-1 && r<b;i=edge[i].next){
 52         int u=edge[i].e;
 53         if(edge[i].v>0 && d[u]==d[a]+1){
 54             double x=find_min(edge[i].v,b-r);
 55             x=dfs(u,x);
 56             r+=x;
 57             edge[i].v-=x;
 58             edge[i^1].v+=x;
 59         }
 60     }
 61     if(!r)d[a]=-2;
 62     return r;
 63 }
 64 
 65 int dinic(int sp,int tp){
 66     int total=0,t;
 67     while(bfs()){
 68         while(t=dfs(sp,MAX))
 69         total+=t;
 70     }
 71     return total;
 72 }
 73 
 74 int main()
 75 {
 76     int t,i,n,m,a,b,c;
 77     int x,y;
 78     scanf("%d",&t);
 79     while(t--)
 80     {
 81         scanf("%d%d",&n,&m);
 82         int min_x=MAX,max_x=-MAX;
 83         int min_id=1,max_id=1;
 84         for(i=1;i<=n;i++){
 85             scanf("%d%d",&x,&y);
 86             if(x<min_x){
 87                 min_x = x;
 88                 min_id = i;
 89             }
 90             if(x>max_x){
 91                 max_x = x;
 92                 max_id = i;
 93             }
 94         }
 95 
 96         sp=min_id; tp=max_id;
 97 
 98         e_num=0;
 99         memset(head,-1,sizeof(head));
100         for(i=1;i<=m;i++){
101             scanf("%d%d%d",&a,&b,&c);
102             AddEdge(a,b,c);
103         }
104 
105         int ans=dinic(sp,tp);
106         printf("%d\n",ans);
107     }
108     return 0;
109 }
原文地址:https://www.cnblogs.com/jackge/p/3021923.html