zoukankan      html  css  js  c++  java
  • BZOJ 3597 SCOI2014 方伯伯送椰子 网络流分析+SPFA

    原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3597

    Description

    四川的方伯伯为了致富,决定引进海南的椰子树。方伯伯的椰子园十分现代化,椰子园中有一套独特的交通系统。

    现在用点来表示交通节点,边来表示道路。这样,方伯伯的椰子园就可以看作一个有 n + 2 个交通节点,m条边的有向无环图。n +1 号点为入口,n +2 号点为出口。每条道路都有 6 个参数,ui,vi,ai,bi,ci,di,分别表示,该道路从 ui 号点通向 vi 号点,将它的容量压缩一次要 ai 的花费,容量扩大一次要 bi 的花费,该条道路当前的运输容量上限为 ci,并且每单位运输量通过该道路要 di 的费用。

    在这个交通网络中,只有一条道路与起点相连。因为弄坏了这条道路就会导致整个交通网络瘫痪,聪明的方伯伯决定绝不对这条道路进行调整,也就是说,现在除了这条道路之外,对其余道路都可以进行调整。

    有两种调整方式:

    1. 选择一条道路,将其进行一次压缩,这条道路的容量会下降 1 单位。

    2. 选择一条道路,将其进行一次扩容,这条道路的容量会上升 1 单位。

    一条道路可以被多次调整。

    由于很久以前,方伯伯就请过一个工程师,对这个交通网络进行过一次大的优化调整。所以现在所有的道路都被完全的利用起来了,即每条道路的负荷都是满的(每条道路的流量等于其容量)。

    但方伯伯一想到自己的海南椰子会大丰收,就十分担心巨大的运输量下,会导致过多的花费。因此,方伯伯决定至少进行一次调整,调整之后,必须要保持每条道路满负荷,且总交通量不会减少。

    设调整后的总费用是 Y,调整之前的总费用是 X。现在方伯伯想知道,最优调整比率是多少,即假设他进行了 k 次调整,(X - Y)/k最大能是多少?

    注:总费用 = 交通网络的运输花费 + 调整的花费

    Input

    第一行包含二个整数N,M接下来M行代表M条边,表示这个交通网络每行六个整数,表示Ui,Vi,Ai,Bi,Ci,Di接下来一行包含一条边,表示连接起点的边

    Output

    一个浮点数,保留二位小数。表示答案,数据保证答案大于0

    Sample Input

    5 10
    1 5 13 13 0 412
    2 5 30 18 396 148
    1 5 33 31 0 39
    4 5 22 4 0 786
    4 5 13 32 0 561
    4 5 3 48 0 460
    2 5 32 47 604 258
    5 7 44 37 75 164
    5 7 34 50 925 441
    6 2 26 38 1000 22

    Sample Output

    103.00

    HINT

    1<=N<=500,0<=M<=3000,1<=Ui,Vi<=N+2,0<=Ai,Bi<=500,0<=Ci<=10000,0<=Di<=1000

    题意概述:
      给出一个N+2个点M条边的DAG图,这是一张网络,每条边有缩容1的代价a,扩容1的代价b,流量上限c,流量费用d。一开始网络中每条边都是满流的。现在可以对网络进行一些调整(不包括和起点相连的唯一的那条边),调整之后使得网络中的所有边依旧满流(即同时流量大小不变)。
      假设进行了K次调整,调整之前的总流量费用为X,调整中花费的费用以及调整之后的流量费用为Y,问(X-Y)/K的最大值(答案保证大于0)。

    分析:
      哎呀呀先分析性质。。。。。
      题目给的(X-Y)/K长得很难看。。发现每一次操作只会让某一条边新的流量的代价增加/减少单位代价,所以说这个式子实际上求的是合法调整方案每次操作的单位代价。
      最终的总流量不能变,那么事实上我们不能增流或者减流,只能调整流量。在残量网络之中一个合法的改流对应了一个环(可以递归证明,因为这是个DAG图所以不存在正权环无效改流的情况)。对于一次增流操作,付出的代价为d+b;对于一次减流操作,得到的收益为d-a。当存在一个环中所有边的调整代价为负数,即得到一个负权环的时候,这种调整给我们带来了收益。
      题目的问法显然是个最优比率问题,那么考虑二分答案。假设当前二分到的值为m,假设存在一种方案,所有被操作的边的收益和为sum,操作边的数量为cnt,那么有sum/cnt>=m,虽然这其中可能有很多个环,但是根据糖水原理一定至少包含一个环单独满足这个性质。于是对于这单独的一个环,式子变成:cnt*m-sum<=0,cnt表示的是环上边的数量。分到每条边的头上就是sum{m-w}<=0,其中w代表的是这条边的收益(原图的边正向连边,收益-(d+b),反向连边,收益为(d-a))。对于原图中没有流量的边,不连反向边。二分答案的时候看有没有负权环,有的话说明答案成立,可以往大猜,否则不成立,只能往小猜。
      二分下界为0,上界为所有边d的和。
      最坏时间复杂度:O((34)*N*M)

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cstdlib>
     5 #include<algorithm>
     6 #include<queue>
     7 #include<set>
     8 #include<map>
     9 #include<vector>
    10 #include<cctype>
    11 #define inf (1e9+5)
    12 using namespace std;
    13 const int maxn=505;
    14 const int maxm=3005;
    15 const double eps=1e-4;
    16 
    17 int N,M;
    18 struct net_edge{ int u,v,a,b,c,d; }NE[maxm];
    19 struct edge{ int to,next; double w; }E[maxm<<1];
    20 int first[maxn],np,inq[maxn],inc[maxn],sum; double dist[maxn];
    21 
    22 void data_in()
    23 {
    24     scanf("%d%d",&N,&M);
    25     for(int i=1;i<=M;i++){
    26         scanf("%d%d%d%d%d%d",&NE[i].u,&NE[i].v,&NE[i].a,&NE[i].b,&NE[i].c,&NE[i].d);
    27         sum+=NE[i].d;
    28     }
    29 }
    30 void add_edge(int u,int v,double w)
    31 {
    32     E[++np]=(edge){v,first[u],w};
    33     first[u]=np;
    34 }
    35 bool SPFA()
    36 {
    37     for(int i=1;i<=N+2;i++) dist[i]=inf,inc[i]=inq[i]=0;
    38     queue<int>q;
    39     q.push(N+1);
    40     dist[N+1]=0,inq[N+1]=1,inc[N+1]=1;
    41     while(!q.empty()){
    42         int i=q.front(); q.pop();
    43         inq[i]=0;
    44         for(int p=first[i];p;p=E[p].next){
    45             int j=E[p].to;
    46             if(dist[i]+E[p].w<dist[j]){
    47                 dist[j]=dist[i]+E[p].w,
    48                 if(!inq[j]){
    49                     if(++inc[j]==N+2) return 1;
    50                     inq[j]=1,q.push(j);
    51                 }
    52             }
    53         }
    54     }
    55     return 0;
    56 }
    57 bool check(double m)
    58 {
    59     memset(first,0,sizeof(first));
    60     np=0;
    61     for(int i=1;i<=M;i++){
    62         add_edge(NE[i].u,NE[i].v,m+(NE[i].d+NE[i].b));
    63         if(NE[i].c) add_edge(NE[i].v,NE[i].u,m-(NE[i].d-NE[i].a));
    64     }
    65     return SPFA();
    66 }
    67 void work()
    68 {
    69     double L=0,R=sum,mid,ans=0;
    70     while(R-L>=eps){
    71         mid=(L+R)/2;
    72         if(check(mid)) ans=mid,L=mid;
    73         else R=mid;
    74     }
    75     printf("%.2f
    ",ans);
    76 }
    77 int main()
    78 {
    79     data_in();
    80     work();
    81     return 0;
    82 }
    View Code
  • 相关阅读:
    c++中的length,strlen;静态数据成员,静态局部变量
    c++中*的含义
    (1)引用的解释; (2)类的成员函数在类外实现的情况
    DbFunctions 作为 LINQ to Entities 查询的一部分使用时,此方法调用规范 CreateDateTime EDM 函数以创建新的 DateTime 对象。
    年视图,选择月份 ---bootstrap datepicker
    时间控件,页面刷新初始化时间控件只显示年月
    load加载层
    response 下载文件火狐浏览器文件名乱码问题
    Web Uploader
    layer是一款近年来备受青睐的web弹层组件
  • 原文地址:https://www.cnblogs.com/KKKorange/p/8601144.html
Copyright © 2011-2022 走看看