HDU 3667 —— Transportation(最小费用流,拆边)

6 篇文章 0 订阅
1 篇文章 0 订阅

题目:Transportation

题意:给定一个有向图,从1号结点运送K单位的物品到N号结点,每条边有一个参数a,如果经过这条边运送x单位的物品,则费用为a*x*x,求最小费用。

常见的费用流是费用*流量,这里流量变成了平方,所以直接套用最小费用流是行不通的。由于题目中每条边的最大流量最多为5,所以可以考虑把边拆开多条流量为1的边,然后费用叠加上去,即费用依次为1*a,3*a,5*a,7*a,9*a,这样叠加起来刚好是对应的平方的值。

然后就可以直接跑费用流的算法了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int N = 110;
const int INF = 0x7fffffff;
#define pb push_back
struct Edge{
    int from,to,cap,flow,cost;
};
vector<Edge> G;
vector<int> V[N];
int n, m, s, t, K, a[N], p[N], d[N];
bool inq[N];
void init(){
    t = n+1;
    G.clear();
    for(int i=0; i<=t; i++) V[i].clear();
}
void add(int from, int to, int cap, int cost){
    G.pb((Edge){from,to,cap,0,cost});
    G.pb((Edge){to,from,0,0,-cost});
    int x = G.size();
    V[from].pb(x-2);
    V[to].pb(x-1);
}
bool BellmanFord(int &flow, int &cost){
    memset(inq,0,sizeof(inq));
    for(int i=0; i<=t; i++) d[i]=INF;
    d[0]=0; inq[0]=1; a[0]=INF; p[0]=-1;
    queue<int> Q;
    Q.push(0);
    while(!Q.empty()){
        int x=Q.front(); Q.pop();
        inq[x]=0;
        for(int i=0; i<V[x].size(); i++){
            int j= V[x][i];
            Edge &e = G[j];
            if(e.cap>e.flow && d[e.to]>d[x]+e.cost){
                d[e.to] = d[x]+e.cost;
                p[e.to] = j;
                a[e.to] = min(a[x], e.cap-e.flow);
                if(!inq[e.to]){
                    Q.push(e.to);
                    inq[e.to]=1;
                }
            }
        }
    }
    if(d[t]==INF)   return 0;
    int x = t;
    flow += a[t];
    cost += d[t]*a[t];
    while(x){
        G[p[x]].flow += a[t];
        G[p[x]^1].flow -= a[t];
        x = G[p[x]].from;
    }
    return 1;
}
int MinCost(int &cost){
    cost=0;
    int flow=0;
    while(BellmanFord(flow,cost));
    return flow==K;
}
int fee[5]={1,3,5,7,9};
int main(){
    while(~scanf("%d %d %d", &n, &m, &K)){
        init();
        int a, b, c, d;
        while(m--){
            scanf("%d %d %d %d", &a, &b, &c, &d);
            for(int i=0; i<d; i++){
                add(a, b, 1, fee[i]*c);
            }
        }
        add(0, 1, K, 0);
        add(n, t, K, 0);
        int cost;
        if(MinCost(cost))   printf("%d\n", cost);
        else    puts("-1");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值