HDU 3667 Transportation(费用流+拆边)

题目链接
题目大意:要从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;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值