题目连接:
https://www.luogu.com.cn/problem/P3381
参考博客:
https://blog.csdn.net/A_Comme_Amour/article/details/79356220
这个博客关于网络流的知识总结的很全面,建议查看
算法:1:Dinic+Spfa
思路:
1:现在我们想象假如我们有一个流量网络,现在每个边除了流量,现在还有一个单位费用(按最短路),这条边的费用相当于它的单位费用乘上它的流量,(因为要多次bfs寻找源点s到汇点t的路径,每一次都可以找到一条流,找到的这条流的流量是确定的,但是这条流的路线却是可以变的,这就是一个求最短路的过程,把求最小费用转化为求最短路径用Spfa算法)我们要保持最大流的同时,还要保持边权最小,这就是最小费用最大流问题
2:因为在一个网络流图中,最大流量只有一个,但是“流法”有很多种,每种不同的流法所经过的边不同因此费用也就不同,所以需要用到最短路算法
3:每次增广的费用就是这次增广的最短路*这次的流量
4:关于Spfa:就是把Dinic中的bfs改成spfa,再求最大流的过程中最小费用流也就求出来了
5:注意:有许多初始化的地方容易漏易错
#include
using namespace std;
const int maxn=5e3+1,maxm=1e5+1;
int n,m,s,t,tot=1,head[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn],maxflow,mincost;
//dis最小花费;pre每个点的前驱;last每个点的所连的前一条边;flow源点到此处的流量
bool vis[maxn];
queueq;
struct edge
{
int to,next,w,dis;//w流量 dis花费
}e[maxm];
void addedge(int x,int y,int w,int dis)
{
e[++tot].to=y;
e[tot].w=w;
e[tot].dis=dis;
e[tot].next=head[x];
head[x]=tot;
}
bool spfa(int s,int t)
{
memset(dis,0x7f,sizeof(dis));
memset(flow,0x7f,sizeof(flow));
memset(vis,0,sizeof(vis));
q.push(s);vis[s]=1;dis[s]=0;pre[t]=-1;
while(!q.empty())
{
int now=q.front();q.pop();
vis[now]=0;
for(int i=head[now];i!=-1;i=e[i].next)
{
int y=e[i].to;
if(e[i].w>0&&dis[y]>dis[now]+e[i].dis)//相当于求最短路
{
dis[y]=dis[now]+e[i].dis;
pre[y]=now;
last[y]=i;
flow[y]=min(flow[now],e[i].w);
if(!vis[y])
{
vis[y]=1;q.push(y);
}
}
}
}
return pre[t]!=-1;
}
void dfs()
{
int now=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
while(now!=s)//从汇点一直回溯到源点
{
e[last[now]].w-=flow[t];
e[last[now]^1].w+=flow[t];
now=pre[now];
}
}
int main()
{
ios::sync_with_stdio(0);
scanf("%d %d %d %d",&n,&m,&s,&t);
memset(head,-1,sizeof(head));//初始化
for(int i=1;i<=m;i++)
{
int x,y,w,dis;scanf("%d %d %d %d",&x,&y,&w,&dis);
addedge(x,y,w,dis);
addedge(y,x,0,-dis); //反边的流量为0,花费是相反数
}
while(spfa(s,t))dfs();
printf("%d %d\n",maxflow,mincost);
return 0;
}