HDU3667.Transportation

转自http://www.acmerblog.com/hdu-3667-transportation-6628.html
该题是最小费用流的变形,只是这里将容量改为了x*x,显然可知直接用模板跑出来的必然是错的,所以这里我就想到了这些走过的边必须得整体考虑,这里我就先用费用流求出了最大流,让后再在结果中寻找容量改变了的边,然后计算出中的花费,显然这也是错误的,因为第三个样例就果断悲剧了,因为该人走的时候每次并不是选择单位费用最少的那条路走,而是要选择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的边不需要拆边,那么这样建图之后求最小费最大流即可,若最大流

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define inf -0x3f3f3f3f
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mem0(a) memset(a,0,sizeof(a))
#define mem1(a) memset(a,-1,sizeof(a))
#define mem(a, b) memset(a, b, sizeof(a))
const int maxn=9999;
typedef long long ll;
int k;
ll flow;

struct Edge{
    int from,to,cap,flow,cost;
    Edge(int u,int v,int c,int f,int w):from(u),to(v),cap(c),flow(f),cost(w){}
};

struct MCMF{
    int n,m,s,t;
    vector<Edge>edges;
    vector<int>G[maxn];
    int d[maxn];    //费用,转化我最短路
    int inq[maxn];  //是否在最短路中
    int a[maxn];    //可以改进的量
    int p[maxn];    //上一条弧

    void init(int n){
        this->n=n;
        for(int i=0;i<=n;i++)
            G[i].clear();
        edges.clear();
    }

    void Addedge(int from,int to,int cap,int cost){
        //cout<<"PPPP"<<endl;
        edges.push_back(Edge(from,to,cap,0,cost));
        edges.push_back(Edge(to,from,0,0,-cost));
        m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool spfa(int s,int t,ll &cost){
        if(flow>=k)
            return false;
        for(int i=0;i<=n;i++)
            d[i]=INF;
        mem0(inq);
        d[s]=0;
        inq[s]=1;
        p[s]=0;
        a[s]=INF;
        queue<int>Q;
        Q.push(s);
        while(!Q.empty()){
            int u=Q.front();
            Q.pop();
            inq[u]=0;
            for(int i=0;i<G[u].size();i++){
                Edge& e=edges[G[u][i]];
                if(e.cap>e.flow&&d[e.to]>d[u]+e.cost){
                    d[e.to]=d[u]+e.cost;
                    p[e.to]=G[u][i];
                    a[e.to]=min(a[u],e.cap-e.flow);
                    if(!inq[e.to]){
                        Q.push(e.to);
                        inq[e.to]=1;
                    }
                }
            }
        }
        if(d[t]==INF){
            return false;
        }
        flow+=a[t];
        cost+=d[t]*a[t]*a[t];
        int u=t;
        while(u!=s){
            edges[p[u]].flow+=a[t];
            edges[p[u]^1].flow-=a[t];
            u=edges[p[u]].from;
        }
        return true;
    }

    ll Mincost(int s,int t){
        flow=0;
        ll cost=0;
        while(spfa(s,t,cost));
        return cost;
    }
};
MCMF ZYC;


int main(){
    int n,m;
    while(scanf("%d%d%d",&n,&m,&k)!=EOF){
        ZYC.init(n);
        int from,to,cap,cost;
        for(int i=0;i<m;i++){
            scanf("%d%d%d%d",&from,&to,&cost,&cap);
            if(cost==0)
                ZYC.Addedge(from,to,cap,cost);
            else{
                for(int j=1;j<=cap;j++)
                    ZYC.Addedge(from,to,1,(2*j-1)*cost);
            }
        }
        if(k!=0)
            ZYC.Addedge(0,1,k,0);
        ll cost1=ZYC.Mincost(1,n);
        if(flow<k)
            puts("-1");
        else{
            printf("%I64d\n",cost1);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值