hdu 3667 Transportation 费用流

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 a  i. If you want to carry x units of goods along this road, you should pay a  i * x  2 dollars to hire guards to protect your goods. And what’s worse, for each road i, there is an upper bound C  i, which means that you cannot transport more than C  iunits 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 (u  i, v  i, a  i, C  i), indicating there is a directed road from city u  i to v  i, whose coefficient is a  i and upper bound is C  i. (1 <= u  i, v  i <= N, 0 < a  i <= 100, C  i <= 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
 一下题意和思路是摘抄别人的

题意描述:一个人携带k单位的东西要从城市1安全到达n,有m条有向的路径连接这n个城市,该人从每条路上经过时可能会遇到劫匪,那么这条路的危险系数为a,那么他要安全的经过该条路径,就需要花a*x*x的费用去雇佣guard(这里x表示该人携带的多少单位的物品),且每条路径上的最多可以携带c单位的物品通过(注意这里c<=5),现在要求最少的费用使他能够安全到达n,若不能到达则输出-1;


分析:该题是显然的费用流题目的变形,因为这里容量变成了x*x了,那么这就用最小费流求出来的值将是错误的,因为2*2!=1*1+1*1的,所以这里我就想到了这些走过的边必须得整体考虑,这里我就先用费用流求出了最大流,让后再在结果中寻找容量改变了的边,然后计算出中的花费,显然这也是错误的,因为第三个样例就果断悲剧了,因为该人走的时候每次并不是选择单位费用最少的那条路走,而是要选择c*x*x的最小的那条边走,所以这样求出来的值是错的,这时候我猛然发现c<=5这个条件没有用,这里我就想到了拆边,若要体现每次都是选择都是在前面选择的基础上进行的,每次求的增广路都是c*x*x代表的权值中最小的,那么基本思路就出来了,将每条容量为w的边拆成容量为1的w条边,这里费用为0的边,只需要建一条边即可,因为这条边不管携带多少单位的东西经过不需要任何费用也是安全的,最后新建一个源点和节点1相连容量为k,费用为0,那么求最小费用最大流即是解,现在剩下的问题就是权值问题了,刚开始我将每条拆开的权值设为c*j*j,这样建图之后用费用流求出最大流后,先判断是否能全部运过去,这里原先有m条边,那么就被拆成了m组边,我想用最小费用流之后在每一组边中找到容量为0且费用最高的那条边然后每组求和,这样也是错的,因为当用最短路进行增广的时候,选择路径的时候就可能不是最好的,因为该边开始已径流过i次了,那么第i+1次流过的费用就应该是(i+1)^2-(i*i),但是我这里的权值却是(i+1)^2,那么在选择路径增广的时候就有可能出现两条边的x*x+y*y<(i+1)^2但是(x*x)-(x-1)^2+(y*y)-(y-1)^(y-1)>(i+1)^2-(i*i),那么这个时候选择错误!!!!!

建图的方法: 新建源点,源点到1连边容量为k,费用为0,将给出的每条边拆成w条容量为1的边每一条的费用为((i+1)^2-(i*i))*c=(2*i-1)*c,费用为0的边不需要拆边,那么这样建图之后求最小费最大流即可,若最大流<k那么表示不能够到达n,则输出-1,否则输出最小费用,注意这里当k=0时,假如图不是连通的,那么则应该输出-1,但是按以上方法输出的为0,我试了一下没有这样的数据!!


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#define INF 0x3f3f3f3f
using namespace std;
int head[2000],vis[2000],dis[2000],pre[2000];
int cnt,s,T,ans,ss;
struct node
{
    int u,v,w,f,next;
} edge[50000];
void add(int u,int v,int w,int f)
{
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].f=f;
    edge[cnt].next=head[u];
    head[u]=cnt++;

    edge[cnt].u=v;
    edge[cnt].v=u;
    edge[cnt].w=0;
    edge[cnt].f=-f;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}
int SPFA()
{
    int i;
    memset(pre,-1,sizeof(pre));
    memset(vis,0,sizeof(vis));
    for(i=0; i<=T; i++)
        dis[i]=INF;
    queue<int>q;
    dis[s]=0;
    vis[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        i=head[u];
        vis[u]=0;
        while(i!=-1)
        {
            if(edge[i].w>0&&dis[edge[i].v]>dis[u]+edge[i].f)
            {
                dis[edge[i].v]=dis[u]+edge[i].f;
                pre[edge[i].v]=i;
                if(!vis[edge[i].v])
                {
                    vis[edge[i].v]=1;
                    q.push(edge[i].v);
                }
            }
            i=edge[i].next;
        }
    }
    if(pre[T]==-1)
        return 0;
    return 1;
}
void MincostMaxFlow()
{
     ans=0,ss=0;
    while(SPFA())
    {
        int maxl=INF;
        int p=pre[T];
        while(p!=-1)
        {
            maxl=min(maxl,edge[p].w);
            p=pre[edge[p].u];
        }
        p=pre[T];
        while(p!=-1)
        {
            edge[p].w-=maxl;
            edge[p^1].w+=maxl;
            p=pre[edge[p].u];
        }
        ss+=maxl;
        ans+=maxl*dis[T];
    }
}

int main()
{
    int n,m,k;
    int x,y,c,w;
    while(scanf("%d %d %d",&n,&m,&k)!=EOF)
    {
        memset(head,-1,sizeof(head));
        cnt=0;
        for(int i=0;i<m;i++)
        {
            scanf("%d %d %d %d",&x,&y,&c,&w);
            if(c==0)
                add(x,y,w,0);
            else
            {
                for(int j=1;j<=w;j++)
                add(x,y,1,(2*j-1)*c);
            }
        }
        s=0;
        T=n;
            add(s,1,k,0);
       MincostMaxFlow();
       if(ss<k)
        printf("-1\n");
       else
       printf("%d\n",ans);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值