bzoj3597/loj2214/洛谷P3288 方伯伯运椰子 分数规划+spfa判负权环

一看这题感觉要分数规划,于是迅速上套路:
二分一个答案lim,如果存在 X Y k l i m ,则存在 ( X Y ) l i m k 0
然后发现压缩相当于网络流中的退流,扩容则相当于增广。当然也可以本质的理解,一开始所有点上的“囤积流量”都是0,如果压缩,那么v的“囤积流量”-1,u的“囤积流量”+1。扩容则反之。最后搞了一通后的目标状态也是所有点的“囤积流量”都是0,这样就可以看作把一个数字-1不断地从这个点移动到那一个点,最后要移回那个一开始被+1的点。
那么就可以考虑这样一个模型,连边(u,v,b+d)和(v,u,a-d),分别表示扩容后的费用变化和压缩后的费用变化。
又发现修改次数就是经过了多少条边,也就是边权可以再同时减去lim,求是否存在正权环。
正权环我不知道怎么求,但是可以把所有边权取相反数求有没有负权环(或权值为0的环)。
你以为这样就结束了?并没有,显然每条边可以进行压缩的量是有限制的。
但是经过网络流的思想思考,退流增广一次和退流增广若干次并不改变正负性,所以每一次退流或增广都只要进行一次。因此我们只要对于c=0的情况,不建立表示压缩的边即可。
代码:

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
    int q=0;char ch=' ';
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
    return q;
}
typedef double db;
const db eps=1e-9;
const int N=5005,M=6005;
int n,m,tot,S,T;
int h[N],ne[M],to[M],inq[N],num[N];db w[M],dis[N];
void add(int x,int y,db z) {to[++tot]=y,ne[tot]=h[x],h[x]=tot,w[tot]=-z;}
int spfa(db lim) {
    queue<int> q;
    for(RI i=1;i<=n;++i) q.push(i),inq[i]=num[i]=1,dis[i]=0;
    while(!q.empty()) {
        int x=q.front();q.pop(),inq[x]=0;
        for(RI i=h[x];i;i=ne[i])
            if(dis[x]+lim-w[i]<=dis[to[i]]+eps) {
                dis[to[i]]=dis[x]+lim-w[i];
                if(!inq[to[i]]) {
                    inq[to[i]]=1,++num[to[i]],q.push(to[i]);
                    if(num[to[i]]==n) return 1;
                }
            }
    }
    return 0;
}
int main()
{
    db l=0,r=1e7;int x,y,a,b,c,d;
    n=read()+2,m=read();
    for(RI i=1;i<=m;++i) {
        x=read(),y=read(),a=read(),b=read(),c=read(),d=read();
        add(x,y,b+d);
        if(c!=0) add(y,x,a-d);
    }
    while(fabs(r-l)>1e-3) {
        db mid=(l+r)/2.0;
        if(spfa(mid)) l=mid;
        else r=mid;
    }
    printf("%.2lf\n",l);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/litble/article/details/80135719