题目链接
题目大意:要从1到n运送k单位货物,每条边都有一个流量限制,和通过这条边要花费一定的钱,这个花费是a*x^2其中a是费用系数,输入中给出,x为通过这条边的流量,求从1到n的最小花费。
分析:由于题目中的流量是一种最小性参数,所以普通的最短路径必然是解决不了这个问题。所以就可以想到网络流当中的费用流,要运送k单位货物也就是说流量为k时即可停止增广,然后每次按最短路径去增广,这样增广出来的就是最小费用。
然后注意到每条边是有费用系数的且和流量成平方关系,所以拆边,有多少容量就拆成几条边,并且费用各异,比如容量为5的边,可以拆成五条边,费用分别为1a,3a,5a,7a,9a,这样在流量为1的时候只经过1a,在2的时候经过1a和3a,依此类推。
然后注意到k可能取0,当k为0直接输出0,不然会超时,然后要让流量增广到k就立刻停止。
#include<bits/stdc++.h>
#define PII pair<int,int>
#define x first
#define y second
#define MAIN main
using namespace std;
typedef long long LL;
const int INF=0x3f3f3f3f;
const int N=1e2+10;
struct edge
{
int u,v,cap,flow,cost;
edge(int u=0,int v=0,int cap=0,int flow=0,int cost=0):u(u),v(v),cap(cap),flow(flow),cost(cost){}
};
vector<edge>E;
vector<int>g[N];
void add(int u,int v,int cap,int cost)
{
E.push_back(edge(u,v,cap,0,cost));
E.push_back(edge(v,u,0,0,-cost));
int m=E.size();
g[u].push_back(m-2);
g[v].push_back(m-1);
}
int C[5]={1,3,5,7,9};
int dis[N],pre[N],vis[N],a[N],n,m,k;
bool SPFA(int s,int t,int &flow,int &cost)
{
queue<int>q;
for(int i=1;i<=n;i++) dis[i]=INF;
memset(vis,0,sizeof(vis));
a[s]=INF;dis[s]=0;pre[s]=0;vis[s]=1;
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();
vis[u]=0;
for(int i=0;i<(int)g[u].size();i++)
{
edge &e=E[g[u][i]];
int v=e.v;
if(dis[v]>dis[u]+e.cost&&e.cap>e.flow)
{
dis[v]=dis[u]+e.cost;
a[v]=min(a[u],e.cap-e.flow);
pre[v]=g[u][i];
if(!vis[v]) vis[v]=1,q.push(v);
}
}
}
if(dis[t]==INF) return false;
flow+=a[t];
cost+=a[t]*dis[t];
int u=t;
while(u!=s)
{
E[pre[u]].flow+=a[t];
E[pre[u]^1].flow-=a[t];
u=E[pre[u]].u;
}
return true;
}
int mincost(int s,int t,int &flow)
{
int cost=0;
while(SPFA(s,t,flow,cost))
{
if(flow==k) return cost;
}
return cost;
}
int MAIN()
{
while(scanf("%d%d%d",&n,&m,&k)==3)
{
E.clear();
for(int i=1;i<=n;i++) g[i].clear();
for(int i=1;i<=m;i++)
{
int u,v,a,c;
scanf("%d%d%d%d",&u,&v,&a,&c);
for(int j=0;j<c;j++){
add(u,v,1,C[j]*a);
}
}
if(k==0)
{
printf("0\n");
continue;
}
int flow=0;
int ans=mincost(1,n,flow);
if(flow<k) printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}