【USACO】草地排水

时间:2023-03-08 17:33:24

Drainage Ditches 草地排水 usaco 4.2.1
描述
在农夫约翰的农场上,每逢下雨,Bessie最喜欢的三叶草地就积聚了一潭水。这意味着草地被水淹没了,并且小草要继续生长还要花相当长一段时间。因此,农夫约翰修建了一套排水系统来使贝茜的草地免除被大水淹没的烦恼(不用担心,雨水会流向附近的一条小溪)。作为一名一流的技师,农夫约翰已经在每条排水沟的一端安上了控制器,这样他可以控制流入排水沟的水流量。
农夫约翰知道每一条排水沟每分钟可以流过的水量,和排水系统的准确布局(起点为水潭而终点为小溪的一张网)。需要注意的是,有些时候从一处到另一处不只有一条排水沟。
根据这些信息,计算从水潭排水到小溪的最大流量。对于给出的每条排水沟,雨水只能沿着一个方向流动,注意可能会出现雨水环形流动的情形。

格式
PROGRAM NAME:ditch
INPUT FORMAT:
(file ditch.in)
第1行: 两个用空格分开的整数N (0 <= N <= 200) 和 M (2 <= M<= 200)。N是农夫John已经挖好的排水沟的数量,M是排水沟交叉点
的数量。交点1是水潭,交点M是小溪。
第二行到第N+1行: 每行有三个整数,Si, Ei, 和 Ci。Si 和 Ei (1 <=Si, Ei <= M) 指明排水沟两端的交点,雨水从Si 流向Ei。Ci (0 <= Ci<= 10,000,000)是这条排水沟的最大容量。
OUTPUT FORMAT:
(file ditch.out)
输出一个整数,即排水的最大流量。
SAMPLE INPUT

5 4
1 2 40
1 4 20
2 4 20
2 3 30
3 4 10
SAMPLE OUTPUT
50

最大流裸题,被教练拖出来练了两次,一次用邻接矩阵,一次用链式前向星,具体看代码注释吧。

 #include <algorithm>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
ifstream fin("ditch.in");
ofstream fout("ditch.out");
int dis[][]={};//邻接矩阵 存图
int far[]={};//距离源点的距离
int point=,bians=,ans=;
bool BFS();//寻找最短路
int find(int nw,int least);//寻找路径流通
int mn(int a,int b);//返回较小数的函数
int main(void)
{
fin>>bians>>point;
int a=,b=,pay=;
for(int i=;i<=bians;i++)
{
fin>>a>>b>>pay;
dis[a][b]+=pay;//构图
}
int liul=;
while(BFS())
{
while(liul=find(,0x7fffffff))//寻找可流通的路径【即所谓增广路】
{
ans+=liul;
}
}
fout<<ans;
return ;
} bool BFS()
{
int duil[]={},head=,tail=;
duil[head+]=;
memset(far,-,sizeof(far));
far[]=;
do
{
head++;
if(head==)head=;//循环队列处理
for(int i=;i<=point;i++)
{
if(dis[duil[head]][i]>&&far[i]<)
{
far[i]=far[duil[head]]+;
tail++;
if(tail==)tail=;//循环队列处理
duil[tail]=i;
}
}
}while(head!=tail);
if(far[point]>)return ;//如果找到到终点的路径,返回1
return ;//否则返回0
} int find(int nw,int least)//least代表当前可行的最大流量
{
if(nw==point)return least;
int run=,used=,syu=least;//used表示当前节点可流出的最大流量,syu代表当前可流出的流量
for(int i=;i<=point;i++)
{
if(dis[nw][i]>&&far[i]==far[nw]+)//如果当前点到第i点联通且当前点是路径的下一个点
{
run=find(i,mn(syu,dis[nw][i]));//递归寻找当前路径的最大流量
if(run!=)
{
dis[nw][i]-=run;//正弧减去最大流量
dis[i][nw]+=run;//反弧加上最大流量
syu-=run;//减去当前可流出流量
used+=run;//总流量加上当前增广路可流出流量
}
}
}
return used;
} int mn(int a,int b)
{
if(a>b)return b;
else return a;
}

链式前向星版

 #include <algorithm>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cmath>
using namespace std;
ifstream fin("ditch.in");
ofstream fout("ditch.out");
struct ls
{
int nw;//当前标号
int to;//从nw到to有一条边
int rl;//当前可行容量
int nxt;//连接的下一条弧【注:根据链式前向星的规定,nxt指下一条弧在数组中的下标】
int fan;//反向弧的数组下标
};
ls qxq[];//链式前向星存储
int dis[]={};//当前节点距终点的距离
int tou[]={};//链式前向星的head数组
int point=,bians=,ans=,bian=;
bool BFS();//根据SPFA算法寻找最短路
int Dinic(int xz,int least);//进行水量输送与判断
int mn(int a,int b);//返回较小数的函数
void add(int fr,int to,int ll);//构建链式前向星
int main(void)
{
fin>>bians>>point;
memset(tou,-,sizeof(tou));
int a=,b=,c=;
for(int i=;i<=bians;i++)
{
fin>>a>>b>>c;
add(a,b,c);//构建链式前向星
}
int liul=;
while(BFS())//如果一次BFS没有寻找到可以到终点的路径,则算法结束。
{
while(liul=Dinic(,0x7fffffff))//一次BFS就不断寻找从起点到终点的路径并更改容量
{
ans+=liul;
}
}
fout<<ans;
return ;
} void add(int fr,int to,int ll)
{
qxq[bian].nw=fr;
qxq[bian].to=to;
qxq[bian].nxt=tou[fr];
tou[fr]=bian;
qxq[bian].rl=ll;
qxq[bian].fan=bian+;
bian++;//构建一条弧
qxq[bian].nw=to;
qxq[bian].to=fr;
qxq[bian].nxt=tou[to];
tou[to]=bian;
qxq[bian].rl=;
qxq[bian].fan=bian-;
bian++;//构建它的反向弧【注:这里我们不考虑从A到B有一条弧,B到A有另一条弧的情况】
//事实上,就算不考虑,算法对上述情况也可以正确运行
//至于原理何在,就交给读者自己思考(因为我也不知道)(划去)
} bool BFS()
{
int dl[]={},head=,tail=,bh=;
dl[head+]=;
memset(dis,-,sizeof(dis));
dis[]=;
do
{
head++;
if(head==)head=;
for(int i=tou[dl[head]];i>;i=qxq[i].nxt)
{
bh=qxq[i].to;
if(dis[bh]<&&qxq[i].rl>)
{
tail++;
if(tail==)tail=;
dl[tail]=bh;
dis[bh]=dis[dl[head]]+;
}
}
}while(head!=tail);
//看起来很像SPFA算法其实不是,因为这里一点出了队列就不可能再一次进入队列了
//所以准确的说,应该叫BFS更加贴切
if(dis[point]<=)return ;//如果没有找到路径就返回0
return ;
} int Dinic(int xz,int least)//least代表当前可行流量
{
if(xz==point)return least;//如果当前点已经是终点就返回当前流量
int run=,bh=,used=,syu=least;//同邻接矩阵版注释
for(int i=tou[xz];i>;i=qxq[i].nxt)
{
bh=qxq[i].to;//枚举下一个点
if(dis[bh]==dis[xz]+)//如果当前点是最短路径的下一个点
{
run=Dinic(bh,mn(syu,qxq[i].rl));//递归计算可行流量
if(run!=)
{
qxq[i].rl-=run;//正弧减去可行流量
qxq[qxq[i].fan].rl+=run;//反弧加上可行流量
syu-=run;//当前可用流量减去流出流量
used+=run;//当前流出总流量加上流出流量
}
}
}
return used;
} int mn(int a,int b)
{
if(b>a)return a;
else return b;
}