hdu 3667 Transportation【最小费用流+拆边】

本文介绍了一种解决特定运输问题的最优费用流算法实现,通过分解边流并优化路径选择,确保在限定条件下以最低成本完成指定数量货物的运输。

Transportation

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2820    Accepted Submission(s): 1202

Problem Description

There are N cities, and M directed roads connecting them. Now you want to transport K units of goods from city 1 to city N. There are many robbers on the road, so you must be very careful. The more goods you carry, the more dangerous it is. To be more specific, for each road i, there is a coefficient ai. If you want to carry x units of goods along this road, you should pay ai * x2 dollars to hire guards to protect your goods. And what’s worse, for each road i, there is an upper bound Ci, which means that you cannot transport more than Ci units of goods along this road. Please note you can only carry integral unit of goods along each road.
You should find out the minimum cost to transport all the goods safely. 

Input

There are several test cases. The first line of each case contains three integers, N, M and K. (1 <= N <= 100, 1 <= M <= 5000, 0 <= K <= 100). Then M lines followed, each contains four integers (ui, vi, ai, Ci), indicating there is a directed road from city ui to vi, whose coefficient is ai and upper bound is Ci. (1 <= ui, vi <= N, 0 < ai <= 100, Ci<= 5)

Output

Output one line for each test case, indicating the minimum cost. If it is impossible to transport all the K units of goods, output -1.

Sample Input

2 1 2

1 2 1 2

2 1 2

1 2 1 1

2 2 2

1 2 1 2

1 2 2 2

 

 

Sample Output

4

-1

3

Source

2010 Asia Regional Harbin

 

题目大意:

有n个城市,有m条有向边,需要送k单位的货物从城市1到城市n,对应每条边输入四个元素,u,v,a,c分别表示起点,终点和单位花费以及最大流量。对应假如我要从某条边经过并且运送z单位的货物,那么需要权值花费:a*z*z;

问能否完成任务,如果可以求一个最小权值花费,否则输出-1。


思路:


1、首先分析样例,明确这样一点,如果想完成任务的同时花费最少,直接将m条无向边就那样加入到网络中跑费用流是不行的。


2、那么这样考虑一条边:

①假设这条边的输入是:u,v,2,3;

②那么如果想从这条边运送流为1的货物,花费即:2*1*1;

③那么如果想从这条边运送流为2的货物,花费即:2*2*2;也相当于其在这条边运送了流为1的货物之后再运送流为1的货物:2*1*1+2*2*2-2*1*1;

④那么如果想从这条边运送流为3的货物,花费即:2*3*3;也相当于其在这条边运送了流为2的货物之后再运送流为1的货物:2*2*2+2*3*3-2*2*2;

⑤那么我们其实可以将一个流为3的边,看成三条流为1的边,其三条流为1的边的权值分别为:1*1*1,2*2*2-2*1*1,2*3*3-2*2*2;


3、那么这个时候我们再跑费用流的时候就能够得到花费最少了。那么最终建图方式如下:

①设定1为源点,n为汇点。

②将m条有向边加入到网络中,一条流为f的边,拆成f条流为1的边,其权值为上述方式求得。


4、建好图之后跑费用流,如果当流大于等于k的时候,跳出即可。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
struct node
{
    int from;
    int to;
    int w;
    int f;
    int next;
    int num;
}e[5000*1500];
int head[5000];
int pre[5000];
int path[5000];
int dis[5000];
int vis[5000];
int n,m,k,cont,ss,tt;
void add(int from,int to,int w,int f)
{
    e[cont].to=to;
    e[cont].w=w;
    e[cont].f=f;
    e[cont].num=cont;
    e[cont].next=head[from];
    head[from]=cont++;
}
void getmap()
{
    ss=1;tt=n;
    cont=0;
    memset(head,-1,sizeof(head));
    for(int i=0;i<m;i++)
    {
        int u,v,a,c;
        scanf("%d%d%d%d",&u,&v,&a,&c);
        for(int j=1;j<=c;j++)
        {
            add(u,v,a*j*j-a*(j-1)*(j-1),1);
            add(v,u,-(a*j*j-a*(j-1)*(j-1)),0);
        }
    }
}
int SPFA()
{
    queue<int >s;
    for(int i=1;i<=n;i++)dis[i]=0x3f3f3f3f;
    dis[ss]=0;
    memset(vis,0,sizeof(vis));
    s.push(ss);
    while(!s.empty())
    {
        int u=s.front();
        s.pop();
        vis[u]=0;
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            int f=e[i].f;
            if(f&&dis[v]>dis[u]+w)
            {
                pre[v]=u;
                path[v]=e[i].num;
                dis[v]=dis[u]+w;
                if(vis[v]==0)
                {
                    vis[v]=1;
                    s.push(v);
                }
            }
        }
    }
    if(dis[tt]==0x3f3f3f3f)return 0;
    else return 1;
}
void MCMF()
{
    int ans=0;
    int maxflow=0;
    while(SPFA()==1)
    {
        int minn=0x3f3f3f3f;
        for(int i=tt;i!=ss;i=pre[i])
        {
            minn=min(minn,e[path[i]].f);
        }
        for(int i=tt;i!=ss;i=pre[i])
        {
            e[path[i]].f-=minn;
            e[path[i]^1].f+=minn;
        }
        maxflow+=minn;
        ans+=dis[tt]*minn;
        if(maxflow>=k)break;
    }
    if(maxflow>=k)printf("%d\n",ans);
    else printf("-1\n");
}
int main()
{
    while(~scanf("%d%d%d",&n,&m,&k))
    {
        getmap();
        MCMF();
    }
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值