zoukankan      html  css  js  c++  java
  • bzoj 3597: [Scoi2014]方伯伯运椰子

    Description

     

    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<=5000

    0<=M<=3000
     
    1<=Ui,Vi<=N+2

    0<=Ai,Bi<=500

    0<=Ci<=10000

    0<=Di<=1000
     
    很久之前就想做这道题了...
    上午叶大佬给我看了APIO2017 T3后,特别想找一道0/1分数规划打一打
    这个题首先分数规划的思路是比较显然的...设答案为mid,wi为一次操作的贡献,n为次数;
    X-Y>=mid*n;
    X-(X+wi)>=mid*n=mid*1;
    X-(X+wi+mid)>=0;
    wi+mid<=0;
    那么我们就是需要判断∑wi+mid的最小值<=0;
    首先可以很好的知道缩一条边的贡献是ai-di,扩大一条边的贡献是bi+di;
    但是我们必须满足流量不变且每条边满载,
    然后我发现修改一条边是牵一发而动全身的,为了一条边需要修改一连串的边且要一直到起点出来的那条边.
    然后就觉得很完蛋...其实好像可以用判负圈那套理论...
    于是在网上看到了一种很有道理的想法.是一种基于调整的思路.
    假设先把所有的道路压缩为0,那么初始费用为:∑(ai-di+mid)*ci;
    然后用费用流进行调整:
    对于原图中的每条边进行两种调整:
    1.连容量为c,费用为-(ai-di+mid)的边;
    2.连容量为Inf,费用为(bi+di+mid)的边;(从起点出发的不连)
    对于第一种边表示撤回原来的压缩操作,对于第二种边表示进行扩大操作.
     
    这样做的正确性:
    1.由于是最小费用最大流所以会优先增广第一种费用小的边,所以一定是把所有压缩操作撤回了,再考虑扩大操作的
    2.由于是最小费用最大流,所以新图的最大流和原图相等,流量平衡了
    妙不可言
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define RG register
    using namespace std;
    const int N=100000;
    const int Inf=19260817;
    const double eps=1e-5;
    int head[N],nxt[N],to[N],s[N],fa[N],cnt=1,n,m,S,T,tt;
    int w[N],ti[N],vis[N],q[N*10];
    double c[N],dis[N],cost,ans;
    struct data{
        int u,v,a,b,c,d;
    }a[N];
    inline void Addedge(RG int x,RG int y,RG int z,RG double v){
        to[++cnt]=y,nxt[cnt]=head[x],s[cnt]=z,c[cnt]=v,head[x]=cnt;
    }
    inline void lnk(RG int x,RG int y,RG int z,RG double v){
        Addedge(x,y,z,v);Addedge(y,x,0,-v);
    }
    inline bool spfa(){
        for(RG int i=1;i<=T;i++) vis[i]=0,dis[i]=Inf;
        int t=0,sum=1;q[0]=S,vis[S]=1,dis[S]=0;
        while(t<sum){
    	int now=q[t++];vis[now]=0;
    	for(RG int i=head[now];i;i=nxt[i]){
    	    int y=to[i];
    	    if(dis[y]>dis[now]+c[i]&&s[i]){
    		fa[y]=i;dis[y]=dis[now]+c[i];
    		if(!vis[y]) vis[y]=1,q[sum++]=y;
    	    }
    	}
        }
        if(abs(dis[T]-Inf)<=eps) return 0;
        int f=Inf;
        for(RG int i=fa[T];i;i=fa[to[i^1]]) f=min(f,s[i]);
        for(RG int i=fa[T];i;i=fa[to[i^1]]) s[i]-=f,s[i^1]+=f;
        cost+=dis[T]*f;
        return 1;
    }
    void rebuild(double mid){
        memset(head,0,sizeof(head));cnt=1;
        for(int i=1;i<=m;i++){
    	lnk(a[i].u,a[i].v,a[i].c,-(-a[i].d+a[i].a+mid));
    	if(a[i].u!=S) lnk(a[i].u,a[i].v,Inf,a[i].b+a[i].d+mid);
        }
    }
    bool check(double mid){
        rebuild(mid);double ret=0;cost=0;
        for(int i=1;i<=m;i++){
    	ret+=(-a[i].d+a[i].a+mid)*a[i].c;
        }
        while(spfa());
        return 0-(ret+cost)>=eps;
    }
    int main(){
        scanf("%d%d",&n,&m);S=n+1,T=n+2;
        for(int i=1;i<=m;i++){
    	scanf("%d%d%d%d%d%d",&a[i].u,&a[i].v,&a[i].a,&a[i].b,&a[i].c,&a[i].d);
        }
        double l=0,r=30000.0;
        while(r-l>=eps){
    	double mid=(l+r)/2;
    	if(check(mid)) ans=mid,l=mid;
    	else r=mid;
        }
        printf("%.2f",ans);
        return 0;
    }
    
     
  • 相关阅读:
    实现一个程序两套快捷键
    SystemC中文教程一
    logback的使用和logback.xml详解
    mysql语句练习50题
    Intellij IDEA中使用Debug调试详解
    用node-webkit把web应用打包成桌面应用
    Idea导入项目详解
    iReport 5.6.0 Error: net.sf.jasperreports.engine.JRException: Error executing SQL statement for : data 最优解决方案
    CentOS 7.X 关闭防火墙
    将 MySQL root 的远程访问密码由空密码改为 password
  • 原文地址:https://www.cnblogs.com/qt666/p/6863985.html
Copyright © 2011-2022 走看看