HDU -- 3667 Transportation(最小费用最大流 )

题目大意:

有n个城市,m条道路,要从城市1运送k单位的货物到城市n,每条路有一个容量上限,路上有小偷,因此每条路上都有一个保障系数a,如果你想在某条路上ai运送x单位的货物,你要支付ai*x*x去雇佣保安来保护你的货物,现在给出m条道路连接的城市,保障系数,容量上限,问最少的花费是多少;

思路分析:

注意:这道题有一个很关键的条件,每条边的最大容量是5;

①:设立一个源点连向城市1,容量为1,费用为0;

②:对于每条给定的边,u,v,a,c,按照容量把边拆成c条边,每条边的容量为1,费用为(2*i-1)*c;

③:让城市n连向汇点,容量为1,费用为0;

这都题我做的真心挺波折,不过收获很大~~看到这道题的时候我发现了容量最大为5的这个条件,于是我就想到拆边,但是我忽视了一个要求从城市1运送到城市n这个条件,于是我是这样建图的,首次设立一个源点,让源点连向各个城市,容量为1,费用为0,然后我把每条边都拆成了5条边,每条边的容量为 i,费用为 i*i*c (i代表第几条边),最后让n*5个点连向汇点。。。于是我果断的练样例都过不去,在这个的建图中存在两个问题,第一个问题是,我是把每条边按容量拆成几部分,容量为i,费用i*i*c,这样的话每条边就都是独立的,就会出现实际流量达不到容量而使费用多算的问题,然后我想着我可以按照实际的流量除一下,但是,费用是按平方计算的啊,就会出现1*1*1+1*2*2/2*1!=1*2*2  这样的不等式(这样的样例, 1 2 2      1 2 1 2),于是我就想到,因为实际一条边的费用是一样的,只是不同的边的费用不一样,所以一条变按容量拆成几条边,就可以让每条边的容量为1,费用就为正常费用减去上一条边的费用,也就是(i^2-(i-1)^2)*c=(2*i-1)*c,这样就被拆成的几条边的费用加回来就还是原本边的花费;第二个问题就是因为要从城市1走到城市n,我那样建图的话如果有一条中间道路,容量足够大,费用足够小,就会从源点直接走过这条道路到终点了,这样就不满足题目要求了。。。。

代码实现:

spfa:858ms

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int N=610;
const int M=51220;
const int INF=0x3f3f3f3f;
int s,t,top,head[N],vis[N],dis[N],minflow[N],path[N],pre[N],sum_cost,sum_flow;

struct Edge{
    int to,next,flow,cost;
    Edge(int _to=0,int _next=0,int _flow=0,int _cost=0):to(_to),next(_next),flow(_flow),cost(_cost){}
}edge[M];

void Addedge(int from,int to,int flow,int cost){
    edge[top]=Edge(to,head[from],flow,cost);
    head[from]=top++;
    edge[top]=Edge(from,head[to],0,-cost);
    head[to]=top++;
}

int Spfa(){
    queue<int> q;
    memset(dis,0x3f,sizeof(dis));
    memset(minflow,0x3f,sizeof(minflow));
    memset(pre,-1,sizeof(pre));
    memset(vis,0,sizeof(vis));
    dis[s]=0;
    q.push(s);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i+1;i=edge[i].next){
            if(edge[i].flow&&dis[edge[i].to]>dis[u]+edge[i].cost){
                dis[edge[i].to]=dis[u]+edge[i].cost;
                pre[edge[i].to]=u;
                path[edge[i].to]=i;
                minflow[edge[i].to]=Min(minflow[u],edge[i].flow);
                if(!vis[edge[i].to]){
                    vis[edge[i].to]=1;
                    q.push(edge[i].to);
                }
            }
        }
    }
    if(dis[t]==INF) return 0;
    sum_cost+=minflow[t]*dis[t];
    sum_flow+=minflow[t];
    int u=t;
    while(u!=s){
        edge[path[u]].flow-=minflow[t];
        edge[path[u]^1].flow+=minflow[t];
        u=pre[u];
    }
    return 1;
}

int main(){
    int n,m,k,u,v,f,c,st;
    while(~scanf("%d%d%d",&n,&m,&k)){
        memset(head,-1,sizeof(head));
        top=sum_cost=sum_flow=0;
        s=0,t=n*6+1;
        //cout<<"s="<<s<<" st="<<st<<" t="<<t<<endl;
        Addedge(s,1,k,0);
        //for(int i=1;i<=n;++i) Addedge(st,i,k,0);
        for(int i=0;i<m;++i){
            scanf("%d%d%d%d",&u,&v,&c,&f);
            for(int j=1;j<=f;++j){
                Addedge(u,v,1,(2*j-1)*c);
            }
        }
        Addedge(n,t,k,0);
        while(Spfa());
        if(sum_flow==k) printf("%d\n",sum_cost);
        else printf("-1\n");
    }
}

zkw:967ms

#include<cstdio>
#include<cstring>
#include<iostream>
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int N=610;
const int M=51220;
const int INF=0x3f3f3f3f;
int s,t,top,head[N],vis[N],cost,ans,flow;

struct Edge{
    int to,next,flow,cost;
    Edge(int _to=0,int _next=0,int _flow=0,int _cost=0):to(_to),next(_next),flow(_flow),cost(_cost){}
}edge[M];

void Addedge(int from,int to,int flow,int cost){
    edge[top]=Edge(to,head[from],flow,cost);
    head[from]=top++;
    edge[top]=Edge(from,head[to],0,-cost);
    head[to]=top++;
}

int aug(int u,int f){
    if(u==t){
        ans+=cost*f;
        flow+=f;
        return f;
    }
    vis[u]=1;
    int tmp=f;
    for(int i=head[u];i!=-1;i=edge[i].next){
        if(edge[i].flow&&!edge[i].cost&&!vis[edge[i].to]){
            int delta=aug(edge[i].to,Min(tmp,edge[i].flow));
            edge[i].flow-=delta;
            edge[i^1].flow+=delta;
            tmp-=delta;
            if(!tmp) return f;
        }
    }
    return f-tmp;
}

bool modlabel(){
    int delta=INF;
    for(int u=s;u<=t;u++){
        if(vis[u]){
            for(int i=head[u];i!=-1;i=edge[i].next)
                if(edge[i].flow&&!vis[edge[i].to]&&edge[i].cost<delta) delta=edge[i].cost;
        }
    }
    if(delta==INF) return 0;
    for(int u=s;u<=t;++u){
        if(vis[u]){
            for(int i=head[u];i!=-1;i=edge[i].next)
                edge[i].cost-=delta,edge[i^1].cost+=delta;
        }
    }
    cost+=delta;
    return 1;
}

void costflow(){
    do{
        do{
            memset(vis,0,sizeof(vis));
        }while(aug(s,INF));
    }while(modlabel());
}

int main(){
    int n,m,k,u,v,f,c,st;
    while(~scanf("%d%d%d",&n,&m,&k)){
        memset(head,-1,sizeof(head));
        top=cost=ans=flow=0;
        s=0,t=n*6+1;
        Addedge(s,1,k,0);
        for(int i=0;i<m;++i){
            scanf("%d%d%d%d",&u,&v,&c,&f);
            for(int j=1;j<=f;++j){
                Addedge(u,v,1,(2*j-1)*c);
            }
        }
        Addedge(n,t,k,0);
        costflow();
        if(flow>=k) printf("%d\n",ans);
        else printf("-1\n");
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值