bzoj1834(网络流加费用流)

12 篇文章 0 订阅
7 篇文章 0 订阅

1834: [ZJOI2010]network 网络扩容

Time Limit: 3 Sec   Memory Limit: 64 MB
Submit: 2390   Solved: 1206
[ Submit][ Status][ Discuss]

Description

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

Input

输入文件的第一行包含三个整数N,M,K,表示有向图的点数、边数以及所需要增加的流量。 接下来的M行每行包含四个整数u,v,C,W,表示一条从u到v,容量为C,扩容费用为W的边。

Output

输出文件一行包含两个整数,分别表示问题1和问题2的答案。

Sample Input

5 8 2
1 2 5 8
2 5 9 9
5 1 6 2
5 1 1 8
1 2 8 7
2 5 4 9
1 2 1 1
1 4 2 1

Sample Output

13 19
30%的数据中,N<=100

100%的数据中,N<=1000,M<=5000,K<=10


解题思路:第一问就是最大流,然后第二问,可以对在建m条边,只是产生费用,再来一个源点,到一的容量为K。跑一边费用流。


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int n,m,k,len,ans;
int from[21000],to[21000],cc[21000],ww[21000],next[21000];
int h[1100];
int dis[1100];
int q[1000000];
bool og[1100];
int pre[1100];
int x[5100],y[5100],c[5100],w[5100];


inline int read()
 {
  char y; int x=0,f=1; y=getchar();
  while (y<'0' || y>'9') {if (y=='-') f=-1; y=getchar();}
while (y>='0' && y<='9') {x=x*10+int(y)-48; y=getchar();}
return x*f;
 }
 
void insert(int x,int y,int c,int w)
 {
  ++len;
  from[len]=x; to[len]=y; cc[len]=c; ww[len]=w; next[len]=h[x]; h[x]=len;
  } 


int dicnic(int sum,int now)
 {
  if (now==n) return sum;
  int u=h[now]; int sug=0;
  while (u!=0)
  {
  if (cc[u]>0 && dis[now]+1==dis[to[u]])
  {
  int s=dicnic(min(cc[u],sum-sug),to[u]);
  if (s>0)
  {
  sug+=s;
     cc[u]-=s; cc[u^1]+=s; 
     if (sug==sum) return sum;
}
}
  u=next[u];
 }
if (sug==0) dis[now]=-1;
return sug;
 }


bool bfs()
 {
  memset(dis,-1,sizeof(dis));
  dis[1]=1; int tail=0,head=0; ++tail; q[tail]=1;
  while (head<tail)
  {
  ++head;
     int u=h[q[head]];
     while (u!=0)
      {
        if (cc[u]>0 && dis[to[u]]==-1)
         {
          dis[to[u]]=dis[q[head]]+1;
          ++tail; q[tail]=to[u];
}
u=next[u];
}
}
if (dis[n]!=-1) return true; else return false;
 }
 
bool spfa()
 {
  memset(dis,0x7e,sizeof(dis));
  memset(og,true,sizeof(og));
  og[0]=false;
  dis[0]=0; int tail=1,head=0; q[tail]=0;
  while (head<tail)
  {
  ++head;
  int u=h[q[head]];
  while (u!=0)
  {
    if (cc[u]>0 && dis[to[u]]>dis[q[head]]+ww[u])
    {
    pre[to[u]]=u;
    dis[to[u]]=dis[q[head]]+ww[u];
    if (og[to[u]])
    {
    og[to[u]]=false;
    ++tail; q[tail]=to[u];
}
}
u=next[u];
}
og[q[head]]=true;
 }
if (dis[n]<1000000) return true;else return false;
  } 
 
void mcf()
 {
  int now=n; int zan=0x7fffffff;
  while (now!=0)
  {
  zan=min(zan,cc[pre[now]]);
  now=from[pre[now]];
 }
now=n;
while (now!=0)
{
cc[pre[now]]-=zan; cc[pre[now]^1]+=zan;
ans+=zan*ww[pre[now]];
now=from[pre[now]];
}
 } 
 
int main()
 {
  n=read(); m=read(); k=read();
  len=1;
  for (int i=1;i<=m;++i)
  {
        x[i]=read(); y[i]=read(); c[i]=read(); w[i]=read();   
        insert(x[i],y[i],c[i],0); insert(y[i],x[i],0,0);
 }
int sum=0;
while (bfs())
{
sum+=dicnic(0x7fffffff,1);
}
printf("%d ",sum);
insert(0,1,k,0); insert(1,0,-k,0); 
for (int i=1;i<=m;++i)
{
insert(x[i],y[i],k,w[i]); insert(y[i],x[i],0,-w[i]); 
}
ans=0;
while (spfa())
{
mcf();
}
printf("%d",ans);
 }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值