bzoj 1834 [ZJOI2010] network 网络扩容 题解

转载请注明:http://blog.csdn.net/jiangshibiao/article/details/22852017

【原题】

1834: [ZJOI2010]network 网络扩容

Time Limit: 3 Sec  Memory Limit: 64 MB
Submit: 1453  Solved: 721
[Submit][Status]

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

HINT

Source


【序言】啦啦啦,第一次做费用流~~先介绍一下。

(最小)费用流的概念:每条边新增一个权——单位费用。在满足最大流的情况下,使总费用最小。

算法:每次用SPFA找到一条花费最短的路。

【分析】第一问就直接dinic吧,正好也熟练一下。

第二问真的是纠结了很长时间(刚做,没什么经验啊)。原来以为应该重新建边,两两可到达的点容量改成K,而费用就是给定的。但是这样会有问题:有可能在残量网络中某些边仍然有流量,这样就不用费用了呀!于是我们可以把第一问做完后的残量网络拿出来,设定每条边的费用是0.然后像刚才说的那样建立一些有费用的边。

【注意】经WCY大牛的指导,我发现一个问题:在做费用流的时候求出的解是满足最大流情况下的解。设第一问的最大流是ANS,要扩容K,有可能在加了新边后最大流大于ANS+K。这样费用可能不是最小的。因此我们可以在1号或N号点新增一条边,容量是K,费用是0。这样就限制了最大流。

【代码】

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=5005;
const int N=1005;
const int INF=2139062143;
struct arr2{int go,s,w,next;}a[M*4];
int f[N],q[N*5],end[N],pre[N];
int ans,flow,n,m,k,cnt,x[M],y[M],c,w,i,money[M];
bool flag[N];
void add(int u,int v,int c,int w)
{
  a[++cnt].go=v;a[cnt].next=end[u];a[cnt].s=c;end[u]=cnt;a[cnt].w=w;
}
bool bfs()
{
  memset(f,-1,sizeof(f));
  memset(q,0,sizeof(q));
  int h=0,t=1;q[1]=1;f[1]=1;
  while (h<t)
  {
    int now=q[++h];if (now==n) return 1;
    for (int i=end[now];i>=0;i=a[i].next)
    {
      int go=a[i].go;
      if (f[go]==-1&&a[i].s)
      {
        f[go]=f[now]+1;
        q[++t]=go;
      }
    }
  }
  return 0;
}
int dinic(int sta,int sum)
{
  if (sta==n) return sum;
  int os=sum;
  for (int i=end[sta];i>=0&&os;i=a[i].next)
  {
    int go=a[i].go;
    if (f[go]==f[sta]+1&&a[i].s)
    {
      int Min=dinic(go,min(os,a[i].s));
      a[i].s-=Min;a[i^1].s+=Min;os-=Min;
    }
  }
  if (os==sum) f[sta]=-1;return sum-os;
}
bool spfa()
{
  int h=0,t=1;
  memset(f,0X7f,sizeof(f));
  memset(q,0,sizeof(q));
  memset(flag,0,sizeof(flag));
  f[1]=0;q[1]=1;flag[1]=true;
  while (h<t)
  {
    int now=q[++h];
    for (int i=end[now];i>=0;i=a[i].next)
    {
      int go=a[i].go;
      if (a[i].s&&f[now]+a[i].w<f[go])
      {
        f[go]=f[now]+a[i].w;pre[go]=i;
        if (!flag[go]) flag[go]=true,q[++t]=go;
      }
    }
    flag[now]=false;
  }
  if (f[n]==INF) return 0;return 1;
}
void cost()
{
  int sum=INF;
  for (int i=pre[n];i>=0;i=pre[a[i^1].go])
  {
    sum=min(sum,a[i].s);
    if (a[i^1].go==1) break;
  }
  for (int i=pre[n];i>=0;i=pre[a[i^1].go])
  {
    a[i].s-=sum;
    a[i^1].s+=sum;
    ans+=sum*a[i].w;
    if (a[i^1].go==1) break;
  }
}
int main()
{
  memset(f,0X7f,sizeof(f));
  scanf("%d%d%d",&n,&m,&k);
  for (i=0;i<=n;i++) end[i]=
  cnt=-1;
  for (i=1;i<=m;i++)
  {
    scanf("%d%d%d%d",&x[i],&y[i],&c,&w);
    money[i]=w;
    add(x[i],y[i],c,0);add(y[i],x[i],0,0);
  }
  flow=0;
  while (bfs())
    flow+=dinic(1,INF);
  printf("%d ",flow);
  for (i=1;i<=m;i++) 
  {
    add(x[i],y[i],INF,money[i]);
    add(y[i],x[i],0,-money[i]);
  }
  add(n,n+1,k,0);add(n+1,n,0,0);n++;
  while (spfa())
    cost();
  printf("%d",ans);
  return 0;
}

bzoj 1834 [ZJOI2010] network 网络扩容 题解,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。