hdu

来源:互联网 发布:朱日和阅兵知乎 编辑:程序博客网 时间:2024/06/09 19:28

Problem Description
Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover patch. This means that the clover is covered by water for awhile and takes quite a long time to regrow. Thus, Farmer John has built a set of drainage ditches so that Bessie's clover patch is never covered in water. Instead, the water is drained to a nearby stream. Being an ace engineer, Farmer John has also installed regulators at the beginning of each ditch, so he can control at what rate water flows into that ditch. 
Farmer John knows not only how many gallons of water each ditch can transport per minute but also the exact layout of the ditches, which feed out of the pond and into each other and stream in a potentially complex network. 
Given all this information, determine the maximum rate at which water can be transported out of the pond and into the stream. For any given ditch, water flows in only one direction, but there might be a way that water can flow in a circle. 
 

Input
The input includes several cases. For each case, the first line contains two space-separated integers, N (0 <= N <= 200) and M (2 <= M <= 200). N is the number of ditches that Farmer John has dug. M is the number of intersections points for those ditches. Intersection 1 is the pond. Intersection point M is the stream. Each of the following N lines contains three integers, Si, Ei, and Ci. Si and Ei (1 <= Si, Ei <= M) designate the intersections between which this ditch flows. Water will flow through this ditch from Si to Ei. Ci (0 <= Ci <= 10,000,000) is the maximum rate at which water will flow through the ditch.
 

Output
For each case, output a single integer, the maximum rate at which water may emptied from the pond. 
 

Sample Input
5 41 2 401 4 202 4 202 3 303 4 10
 

Sample Output
50


题意就是让求最大流的

用 ISAP 算法写吧

首先先介绍一下

ISAP 算法


//1.给每个点标高度 只有高处的水才能往地处流 一开始的高度都为0
//维护距离标号的方法:当找增广路过程中发现某点出发没有允许弧时,将这个点的距离标号设为由它出发的所有弧的终点的距离标号的最小值加一。 
 //这种维护距离标号的方法的正确性我就不证了。由于距离标号的存在,由于"怎么走都是最短路",      
//2.在所有的可行弧中不断的寻找增广路 可行弧的定义为 {(i,j) | dis[i]=dis[j]+1}
//3.遍历完当前节点后(流不出去了) 重新标记当前点的高度(保证下次再来的时候有路可走) dis[i]=min(dis[j])+1
//4.检查是否存在断层 如果出现断层 则图中已不存在增广路 算法可以结束 否则从源点开始继续遍历
//gap[i]数组表示距离汇点标号为i的点有多少个,如果到某一点没有符合距离标号的允许弧,那么需要修改距离标号来找到增广路;
//如果重标号使得gap数组中原标号数目变为0,则算法结束。


代码一:(邻接表vector容器)

#include<stdio.h>    #include<string.h>    #include<algorithm>    using namespace std;    #include<vector>    #define INF 0x3f3f3f3f    struct node    {        int to,cap,rev; // 分别表示 终点,容量,反向边的位置;     };    vector<node> v[300]; // vector 容器结构体类型         int n,m,level[300]; //记录距离标号      int gap[300];   // gap 常数优化;检查是否断层;     int pre[300];   // 记录这个点的前驱;     int s[300];   // 记录 在vector容器的位置;     int hp[300];   //当前弧优化 void init()    {        for(int i=0;i<=n;i++)            v[i].clear();  //每次使用记得清空容器;     }        //入口参数vs源点,vt汇点                    int ISAP(int vs,int vt)    {        memset(gap,0,sizeof(gap));        memset(pre,-1,sizeof(pre));        memset(level,0,sizeof(level));        memset(s,-1,sizeof(s)); memset(hp,0,sizeof(hp));       gap[0]=vt;     // 不要忘了;         int maxflow=0,i,tt=INF;        int u=pre[vs]=vs;        while(level[vs]<vt)        {            node e;            //寻找可行弧            for(i=hp[u];i<v[u].size();i++)            {                e=v[u][i];                if(e.cap>0&&level[u]==level[e.to]+1)  // 一定是会先搜出起点到终点最短的路径;                     break;                }            if(i<v[u].size())            {                pre[e.to]=u;                s[u] = i;hp[u] = i;  // 可行弧当前的位置              u=e.to;                if(e.to==vt)                {                    tt=INF;                     //寻找当前找到的一条路径上的最大流                    for(int j=vt;j!=vs;j=pre[j])                    {                        int k=pre[j];                        if(tt>v[k][s[k]].cap)                            tt=v[k][s[k]].cap;                    }                    maxflow+=tt;                    //更新残留网络                    for(int j=vt;j!=vs;j=pre[j])                    {                        int t, k=pre[j];                        v[k][s[k]].cap-=tt;                        t=v[k][s[k]].rev;                        v[j][t].cap+=tt;                    }                    u=vs;//从源点开始继续搜                  }            }            else            {                //找不到可行弧                int minlevel=vt;                //寻找与当前点相连接的点中最小的距离标号                for(i=0;i<v[u].size();i++)                {                    node pp=v[u][i];                    if(pp.cap>0&&minlevel>level[pp.to])                    { hp[u] = i;   // 重新找到最小标号可行弧的位置;                       minlevel=level[pp.to];                    }                }                                gap[level[u]]--;  //(更新gap数组)当前标号的数目减1;                if(gap[level[u]]==0) //出现断层                    break;                level[u]=minlevel+1;                 gap[level[u]]++;                u=pre[u];    //  从当前点前驱重新扩增;             }        }        return maxflow;    }    int main()    {        int i,j,k,t;        while(~scanf("%d%d",&m,&n))        {            int star,end,cap;            init();            for(i=0;i<m;i++)            {                scanf("%d%d%d",&star,&end,&cap);                v[star].push_back((node){end,cap,v[end].size()});                v[end].push_back((node){star,0,v[star].size() - 1});            }            printf("%d\n",ISAP(1,n));        }        return 0;    }    

代码 二:(邻接矩阵表示)

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;#define INF 0x3f3f3f3f#define Max 300int cap[Max][Max];  // 容量图; int n,m;int level[Max]; // 距离标号; int pre[Max];   //记录当前点的前驱; int gap[Max];   //gap 常数优化,判断是否会出现断层; int hp[Max]; //当前弧; void init(){int i,j;for(i=0;i<=n;i++)for(j=0;j<=n;j++) cap[i][j] = 0;}int ISAP(int vs,int vt)  // 起点和汇点;ISAP邻接表形式; {memset(gap,0,sizeof(gap));memset(level,0,sizeof(level));memset(hp,0,sizeof(hp));memset(pre,-1,sizeof(pre));gap[0] = n;int u;u = pre[vs] = vs;int v,i,j,maxflow=0,minflow = INF;while(level[vs]<n){//寻找可行弧; for(v = hp[u];v<n;v++){if(cap[u][v]>0&&level[u]==level[v]+1)//一定会先搜出从起点到终点的最短路径 break;}if(v<n){pre[v] = u;if(minflow > cap[u][v])  //这样找出来的不一定是当前增广路上最小的minflow minflow = cap[u][v]; // 但一定是当前这个当前距离level[vs] 的最小的minflow//在最下面会给出一组数据,你只要输出每次找到minflow,就知道啥意思; u = hp[u] =v;  // 可行弧当前位置; if(u==vt){for(i=vt;i!=vs;i=pre[i]){cap[pre[i]][i]-=minflow;cap[i][pre[i]]+=minflow;}maxflow += minflow;//printf("minflow==%d\n",minflow);minflow = INF;u=vs;}}else {int minlevel = n;for(i=0;i<n;i++){if(cap[u][i]>0&&minlevel>level[i]){minlevel = level[i];hp[u] = i;  // 重新找到最小标号可行弧的位置; }}gap[level[u]]--;if(gap[level[u]]==0) // 判断断层; return maxflow;level[u] = minlevel+1;gap[level[u]]++;u = pre[u];}}return maxflow;}int main(){int i,j;while(~scanf("%d%d",&m,&n)){init();int star,end,cc;for(i=0;i<m;i++){scanf("%d%d%d",&star,&end,&cc);star--;end--;cap[star][end] += cc; //一定要注意会出现相同边; //cap[end][star] = 0;}printf("%d\n",ISAP(0,n-1));}return 0;}

8 6
1 2 20
2 3 40
3 6 20
1 4 10
2 4 30
4 5 30
3 5 30
5 6 40

30