题目描述
给定一个\(n\)个点\(m\)条边的费用流图,源点为\(1\),汇点为\(n\),求最小费用最大流。
如果第\(i\)条边流量为\(f\),则第\(i\)条边的费用为\(f^2\),注意每条边的流量必须是整数。
输入
第一行包含一个正整数\(T(1\leq T\leq 10)\),表示测试数据的组数。
每组数据第一行包含两个正整数\(n,m(1\leq n\leq 50,1\leq m\leq 500)\)。
接下来\(m\)行,每行三个正整数\(a[i],b[i],c[i](1\leq a[i],b[i]\leq n,a[i]\neq b[i],1\leq c[i]\leq 10)\),表示一条起点是\(a[i]\),终点是\(b[i]\)的边,容量上限是\(c[i]\)。
注意到c[i]很小,同时损失函数是凸函数,考虑拆边,注意拆边的流量应该为1
#include<bits/stdc++.h>
using namespace std;
const int N = 1000,M = 100000,INF = 0x3f3f3f3f;
int h[N],e[M],ne[M],f[M],w[M],idx;
int d[N],flow[N],pre[N];
bool st[N];
queue<int> q;
//a->b一条流量为c,费用为d的边
void add(int a,int b,int c,int d){
e[idx] =b,f[idx] =c,w[idx]= d,ne[idx]= h[a],h[a]= idx++;
e[idx] =a,f[idx] =0,w[idx]=-d,ne[idx]= h[b],h[b] = idx++;
}
bool spfa(int s,int t){
memset(d,0x3f,sizeof d);
memset(flow,0,sizeof flow);
d[s] = 0,flow[s] =0x3f3f3f3f;
q.push(s);
while(q.size())
{
int u = q.front();
q.pop();
st[u]= 0;
for(int i = h[u];i!=-1;i = ne[i])
{
int v =e[i];
if(f[i] && d[v] > d[u]+ w[i])
{
d[v] =d[u] + w[i];
pre[v] =i;
flow[v]= min(f[i],flow[u]);
if(!st[v])
{
q.push(v);
st[v]= 1;
}
}
}
}
return flow[t] > 0;
}
pair<int,int> EK(int s,int t){
int maxflow= 0,mincost =0;
while(spfa(s,t)){
int r = flow[t];
maxflow+= r,mincost += r* d[t];
for(int i = t;i !=s;i = e[pre[i]^ 1]){
f[pre[i]]-=r;
f[pre[i]^ 1] += r;
}
}
return make_pair(maxflow,mincost);
}
int n,m;
void solve()
{
memset(h,-1,sizeof h);
cin>>n>>m;
int s = 1, t = n;
//由于损耗的费用不是常数,同时x^2是一个凸函数
//可以拆边,把费用拆开
for(int i=1;i<=m;++i)
{
int u,v,f;
cin>>u>>v>>f;
for(int j=1;j<=f;++j)
{
//注意此处拆边的时候流量应该变为1
add(u,v,1,j*j-(j-1)*(j-1));
}
}
auto [maxflow,mincost] = EK(s,t);
cout<<maxflow<<' '<<mincost<<'\n';
}
int main()
{
int T;
cin>>T;
while(T--)
{
solve();
}
}
标签:pre,费用,idx,int,flow,最小,leq,越流越
From: https://www.cnblogs.com/ruoye123456/p/18373478