hdu3667 最小费用流 拆边题

23 篇文章 0 订阅
5 篇文章 0 订阅

第一道拆边题  用Bellman-Ford以967ms险过...


先说一下大体的题意:输入N M K 分别指城市的个数 M条边 现在需要把K个单位的物品从城市1运往城市N

接下来的M行  每一行输入四个数 ui, vi, ai, C

代表从城市u到城市v有一条单向路 容量为c 如果在这条路上运送x个单位的物品 需要花费a*x*x的费用 

求最小费用 若不能成功将这K个物品运输到城市N 输出-1


思路:拆边 M条边  对于每一条边 每条边都可以分成c条边

这c条边 容量都为1 花费为 1a  3a 5a 7a 最多到9a(因为c最大是5)

如果流量为1 就会走第一条边 花费a*1*1的费用

如果流量为2 就会走第一条和第二条边  花费为1a+3a == a*2*2

同理依次类推


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<stack>
#include<queue>
#include<cmath>
#include<stack>
#include<list>
#include<map>
#include<set>
typedef long long ll;
using namespace std;
#define MV 11000 //max number of node
#define INF 0x3f3f3f3f

struct edge
{
    int t, cap, cost, rev;
    edge(int to = 0, int c = 0, int ct = 0, int r = 0): t(to), cap(c), cost(ct), rev(r) {};
};
vector <edge> G[MV];
int dis[MV];//the distance from source
int prevv[MV], preve[MV];//the previous node and the previous edge

int min_cost_flow(int v,int s, int t, int f)
{
    
    int ans = 0, i, j;
    while(f > 0)
    {
        fill(dis, dis + v, INF);
        dis[s] = 0;
        bool update = true;
        while(update)
        {//bellman
            update = false;
            for(i = 0; i < v; ++i)
            {
                int size1 = G[i].size();
                if(dis[i] == INF)
                    continue;
                for(j = 0; j < size1; ++j)
                {
                    edge &es = G[i][j];
                    if(es.cap > 0 && dis[es.t] > dis[i] + es.cost)
                    {
                        dis[es.t] = dis[i] + es.cost;
                        prevv[es.t] = i;
                        preve[es.t] = j;
                        update = true;
                    }
                }
            }
        }
        
        if(dis[t] == INF)
            return -1;
        int d = f;
        for(i = t; i != s; i = prevv[i])
            d = min(d, G[prevv[i]][preve[i]].cap);
        
        ans += d * dis[t];
        f -= d;
        for(i = t; i != s; i = prevv[i])
        {
            edge &es = G[prevv[i]][preve[i]];
            es.cap -= d;
            G[es.t][es.rev].cap += d;
        }
    }
    return ans;
}


void addedge(int s1,int t1,int cap,int cost)
{
    G[s1].push_back(edge(t1, cap, cost, G[t1].size()));
    G[t1].push_back(edge(s1, 0, -cost, G[s1].size() - 1));
}
int main()
{
    
    int N,M,K;
    int i;
    
    while(scanf("%d%d%d",&N,&M,&K)==3)
    {
        for(i=0;i<MV;i++)G[i].clear();
        int x,y,a,c;
        while(M--)
        {
            scanf("%d%d%d%d",&x,&y,&a,&c);
            for(i=1;i<=c;i++)
            {
                addedge(x, y, 1, (2*i-1)*a);
            }
        }
        addedge(0, 1, K, 0);
        addedge(N, N+1, K, 0);
        int tt=min_cost_flow( N+2, 0, N+1, K);
        printf("%d\n",tt);
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值