1834: [ZJOI2010]network 网络扩容

题目链接

题目大意:给定一张有向图,每条边都有一个容量C和一个扩容费用W。这里扩容费用是指将容量扩大1所需的费用。求: 1、 在不扩容的情况下,1到N的最大流; 2、 将1到N的最大流增加K所需的最小扩容费用。

题解:
第一问直接上模板
对于第二问,我们可以设图中原有的边费用为0,在残量网络上继续增广。显然残量网络中有流量的边都是有可以直接流的,并且不需要任何费用。
然后我们对于原图中的每一条边,新建一条容量为INF,费用为扩容费用的边。
新建源s,连(s,1,k,0),控制流量。
跑一遍最小费用最大流即可。

我的收获:打板子好累啊……

#include <iostream>
#include <cstdio>
#include <cstring>
#include <climits>
#include <algorithm>
using namespace std;

const int M=100005;
const int MM=200005;
#define INF INT_MAX-100000

bool Exit;
int n,m,k,t,st,ed,ret;
int head[M],dis[M],vis[M],last[M],pre[M];
int d[M],num[M],vi[MM],q[MM],us[MM],uv[MM];

struct edge{int fro,to,val,c,nex;}e[MM];//先费用后流量…… 

void add(int u,int v,int w,int c){e[t]=(edge){u,v,w,c,head[u]};last[u]=head[u]=t++;}

void insert(int u,int v,int w,int c){add(u,v,w,c),add(v,u,-w,0);}

namespace FLOW{

bool spfa()
{
    for(int i=0;i<=ed;i++) vis[i]=0,dis[i]=INF;
    int h=0,w=0;dis[st]=0;q[++w]=st;
    while(h<w){
        int u=q[++h];vis[u]=1;
        for(int i=head[u];i!=-1;i=e[i].nex){
            int v=e[i].to;
            if(e[i].c&&dis[v]>dis[u]+e[i].val){
                dis[v]=dis[u]+e[i].val;
                pre[v]=i;
                if(!vis[v]) vis[v]=1,q[++w]=v;
            }
        }
    }
    return dis[ed]!=INF;
}

void flow(){
    int mx=INF;
    for(int u=ed;u!=st;u=e[pre[u]].fro)
        mx=min(mx,e[pre[u]].c);
    for(int u=ed;u!=st;u=e[pre[u]].fro){
        e[pre[u]].c-=mx,e[pre[u]^1].c+=mx;
        ret+=mx*e[pre[u]].val;
    }
}

int dfs(int x,int in)
{
    if(x==ed) return in;
    int ans=0,f;
    for(int i=last[x];i!=-1;last[x]=i=e[i].nex){
        int v=e[i].to;
        if(e[i].c&&d[v]==d[x]-1){
            f=dfs(v,min(in-ans,e[i].c));
            ans+=f;
            e[i].c-=f,e[i^1].c+=f;
            if(Exit||ans==in) return ans;
        }
    }
    if(--num[d[x]]==0) Exit=1;
    d[x]++,num[d[x]]++,last[x]=head[x];
    return ans;
}

int mflow(){
    int ansx=0;
    while(!Exit) ansx+=dfs(st,INF);
    return ansx;
}

void cflow(){
    while(spfa()) flow();
}

}

void rebuild()
{
    st=0;insert(st,1,0,k);
    for(int i=1;i<=m;i++) insert(us[i],uv[i],vi[i],INF);
}

void work()
{
    cout<<FLOW::mflow()<<" ";
    rebuild();
    FLOW::cflow();
    cout<<ret<<endl;
}

void init()
{
    int x,y,z;
    t=0;memset(head,-1,sizeof(head));
    cin>>n>>m>>k;
    for(int i=1;i<=m;i++)
        scanf("%d%d%d%d",&us[i],&uv[i],&z,&vi[i]),insert(us[i],uv[i],0,z);
    st=1,ed=n,num[0]=n;
}

int main()
{
    init();
    work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值