[bzoj3455]供电网络

来源:互联网 发布:海岛奇兵菊花升级数据 编辑:程序博客网 时间:2024/04/27 00:13

题目描述

阿狸和桃子居住的世界里, 只有一个国家, 这个国家有很多城市, 每个城市直接由中央政府管辖.
电力是这个国家的唯一能源, 但是每个城市的发电能力都不一样, 于是就产生了某些城市电力不足, 而某些城市却电力过剩的情况.
阿狸作为国家的首席工程师, 阿狸的一项重要工作就是均衡整个国家的电力, 使得每个城市的电力都恰好没有剩余或不足.
好在一些城市之间有电线可以输送电力, 这些电线都有自己的输送上限和下限, 并且输送电力的同时会产生大量的热.
每条电线i 发出的热量一定是关于输送电量的一个没有常数项的二次函数,即a_i*x^2+b_i*x, 并且由于电线是二极管做成的, 很显然只能单向输送电力. 每单位热量需要用1 单位的金币来冷却. 任何两个城市之间, 至多有一条电线.
不幸的是, 有时电力网络不像我们想的那么完美, 某些情况下可能无论如何都不能满足整个国家的电力需求. 这种情况下就只好向别的世界购买电力或者将电力输出给别的世界(注意, 每个城市的电力不能有剩余!), 每个城市买入或者输出电力的价格是不一样的(输出也要花钱).
由于阿狸的国家没有小数的概念, 输送,、购买或者交换电力都必须是以整数1 为单位.
阿狸的任务是最小化金币的花费(买入/送出的费用+电线上发热的冷却费用),他最近被这个问题搞得焦头烂额, 以至于没有时间去陪桃子玩, 结果天天被桃子骂T_T. 好在有你, 万能的程序猿, 请你编写一个程序来帮阿狸解决这个问题吧.

最小费用可行流

建图显然:
每个城市建一个点,如果富余j的电量s向i连下限j上限j费用0的边,如果缺失j的电量i向t连下限j上限j费用0的边。向外国买电,s向i连下限0上限inf费用为买电费用的边,向外国送电,i向t连下限0上限inf费用为送电费用的边。对于输电线u向v,是一个凸费用边。一般凸费用边ax^2+bx,上限为L,我们都习惯拆为L条边,容量均为1,费用分别为a+b,3a+b,5a+b……而本题还有下限,u到v下限为拆边方法见我的上下界网络流学习小计,所以ss到v也要连一条凸费用的边,然后u到v的边容量变成r-l了,然而其的费用不是从a+b开始的。所以实现时,每条边保存初始费用以及增减系数(注意是增减系数而不是二次项系数,因为反向弧和正向弧的增减系数是一样的,可以自己思考一下为什么),每次一条边容量-1时,其费用就加上两倍的增减系数,类似容量+1时其费用就减去两倍的增减系数。然后所有的边都能表示为凸费用边的形式(即使其费用函数是一次函数或常数函数),然后这样的实现其实就是动态加边。但是要注意一下反向弧的初始费用以及ss到v的初始费用。详见代码。
然后就是求最小费用可行流了,相当于ss到tt跑最小费用最大流,这里最大流的限制是针对满足下限的,而原图没有流量限制,所以不会出现一个城市不断向外国买电然后向外国送电的情况(简单来说,买电和送电不是必须的,他们下限为0,原图最大流为无穷大,而新图不需要满足他们的流量限制,因为新图最大流不对应原图最大流)
还有一种做法,是把有下限的边的费用拉得很小,按照最小费用的限制就会被强制跑,然后用spfa(zkw不能处理正向弧为负费用的情况)增广直的费用》0。由于我没打过,这里不多说。

#include<cstdio>#include<iostream>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;const int maxn=200+10,maxm=(600+maxn*3)*2*3+10,inf=1000000000;int h[maxn],now[maxn],d[maxn],go[maxm],next[maxm],dis[maxm],L[maxm],co[maxm],fx[maxm],ax[maxm];bool bz[maxn],czy;int i,j,k,l,r,u,v,s,t,n,m,tot,ans,ss,tt,tmp;void add(int x,int y,int z,int c,int a,int d){    go[++tot]=y;    dis[tot]=z;    co[tot]=c;    ax[tot]=a;    fx[tot]=tot+d;    next[tot]=h[x];    h[x]=tot;}void link(int x,int y,int l,int r,int a,int b){    add(ss,y,l,a+b,a,1);    add(y,ss,0,a-b,a,-1);    add(x,tt,l,0,0,1);    add(tt,x,0,0,0,-1);    add(x,y,r-l,(2*l+1)*a+b,a,1);    add(y,x,0,(1-2*l)*a-b,a,-1);}int dfs(int x,int cost){    bz[x]=1;    if (x==tt){        ans+=cost;        return 1;    }    int r=now[x];    while (r){        if (!bz[go[r]]&&dis[r]&&d[x]==d[go[r]]+co[r]){            if (dfs(go[r],cost+co[r])){                dis[r]--;                co[r]+=2*ax[r];                dis[fx[r]]++;                co[fx[r]]-=2*ax[fx[r]];                now[x]=r;                return 1;            }        }        r=next[r];    }    now[x]=0;    return 0;}bool change(){    int tmp=inf,i,r;    fo(i,ss,tt)        if (bz[i]){            r=h[i];            while (r){                if (!bz[go[r]]&&dis[r]&&d[go[r]]+co[r]-d[i]<tmp) tmp=d[go[r]]+co[r]-d[i];                r=next[r];            }        }    if (tmp==inf) return 0;    fo(i,ss,tt)         if (bz[i]) d[i]+=tmp;    return 1;}int main(){    scanf("%d%d",&n,&m);    ss=1;tt=n+4;    s=2;t=n+3;    fo(i,1,n){        scanf("%d%d%d",&j,&k,&l);        if (j>0) link(s,i+2,j,j,0,0);else link(i+2,t,-j,-j,0,0);        link(s,i+2,0,inf,0,k);        link(i+2,t,0,inf,0,l);    }    fo(i,1,m){        scanf("%d%d%d%d%d%d",&j,&k,&l,&r,&u,&v);        link(j+2,k+2,u,v,l,r);    }    link(t,s,0,inf,0,0);    do{        fo(i,ss,tt) now[i]=h[i];        fill(bz+ss,bz+tt+1,0);        while (dfs(ss,0)) fill(bz+ss,bz+tt+1,0);    }while (change());    printf("%d\n",ans);}
0 0