bzoj3698

这道题网上题解到处都是,在此就不赘述了。
看到这题时,我一开始的想法是对于每个点 Ai,j (其中i!=n && j!=n),向其所在行和列连边。
但这样是有问题的,因为可能行和列所连的边流量不同,这显然是不对的。
感觉网络流题目查错一般适合静态差错,因为网络流模板一般不太会打错,bug大多出在见图上,单步跟踪很难发现。
下面就是比q234rty大爷慢到不知道哪里去的代码。
upd:刚才突然发现自己不理解自己的做法了。
事实上,对于有源有汇有上下界的最大流,我在此的做法是先加附加源汇,跑一遍有源有汇有上下界的可行流,再在残量网络上跑最大流。本来答案应该是新增的流量加原始边的流量下界之和,但注意到t->s有一条(0,inf)的边,第一遍跑完后会产生流量为原始边的流量下界之和的反向边,然后第二遍时会把这条边的流量自动加上,就不必手动累加了。

#include <cstdio>
const int N=310,M=100010,inf=1<<30;
struct graph{
    struct edge{
        int to,next,flow;
        edge(int _to=0,int _next=0,int _flow=0):to(_to),next(_next),flow(_flow){}
    }e[M<<1];
    int h[N],d[N],q[N],t,w,xb,n,cur[N],T;
    void addedge(int x,int y,int z){
        e[++xb]=edge(y,h[x],z);
        h[x]=xb;
        e[++xb]=edge(x,h[y],0);
        h[y]=xb;
    }
    inline int o(int x){
        if(x&1)return x+1;
            else return x-1;
    }
    bool bfs(int x,int y){
        for(int i=1;i<=n;++i)d[i]=cur[i]=0;d[x]=1;t=0;w=1;q[1]=x;
        while(t<w){
            int u=q[++t];
            for(int i=h[u];i;i=e[i].next)
                if(e[i].flow && !d[e[i].to]){
                    d[e[i].to] =d[u]+1;
                    q[++w]=e[i].to;
                    if(e[i].to==T)return 1;
                }
        }
        return d[y];
    }
    inline int min(int x,int y){
        return x<y?x:y;
    }
    int dfs(int x,int f){
        if(x==T || !f)return f;int flow=0; 
        for(int&i=cur[x];i;i=e[i].next)if(e[i].flow && d[e[i].to]==d[x]+1){
            int a=dfs(e[i].to,min(f,e[i].flow));
            if(a){
                e[i].flow-=a;e[o(i)].flow+=a;flow+=a;f-=a;
                if(!f)break;
            }
        }
        return flow;
    }
    int maxflow(int x,int y){
        T=y;
        int ans=0;
        while(bfs(x,y)){
            for(int i=1;i<=n;++i)cur[i]=h[i];
            ans+=dfs(x,1<<30);
        }
        return ans;
    }
}g;
int n,i,u[M],v[M],l[M],r[M],x,z,xb,j,k=1,out[M],s;
double y;
int main(){
    scanf("%d",&n);
    u[xb=1]=n<<1,v[1]=1,l[1]=0,r[xb]=inf;
    for(i=1;i<=n;++i)
        for(j=1;j<=n;++j){
            scanf("%lf",&y);x=y;if(y<0 && x!=y)--x;z=x+(x!=y);
            if(i==n && j==n)continue;l[++xb]=x,r[xb]=z;
            if(j==n)u[xb]=1,v[xb]=i+1;
                else if(i==n)u[xb]=j+n,v[xb]=n<<1;
                        else u[xb]=i+1,v[xb]=j+n;
        }
    for(i=1;i<=xb;++i)out[u[i]]+=l[i],out[v[i]]-=l[i];
    for(i=1;i<=n<<1;++i)
        if(out[i]>0)g.addedge(i,n*2+2,out[i]),s+=out[i];
            else if(out[i])g.addedge(n<<1|1,i,-out[i]);
    for(i=1;i<=xb;++i)if(l[i]<r[i])g.addedge(u[i],v[i],r[i]-l[i]);
    g.n=n*2+2;
    if(g.maxflow(n<<1|1,n*2+2)<s)return puts("No"),0;
    printf("%d\n",g.maxflow(1,n<<1)*3);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值