网络流

P4016 负载平衡问题

我们对每个仓库创建两个节点XiX_iXi​ 和YiY_iYi​ ,前者为供应节点,后者为需求节点。求出最终要达到的货物值即平均数,然后把初始值处理成偏移值(初始值-平均数)。

偏移小于0。表明这个节点需要运入货物,将节点的XiX_iXi​ 与源点SSS 相连,容量为偏移,费用为0。
偏移大于0。表明这个节点需要运出货物,将节点的YiY_iYi​ 与汇点TTT 相连,容量为偏移的绝对值,费用为0。

然后再考虑相邻节点的关系。

相邻节点互相补充。将XiX_iXi​ 与YjY_jYj​ 相连,容量为+∞+ \infty+∞ ,费用为1,表示运输单位货物需要1的费用。
不是直接补充,而是作为中间节点转运。将XiX_iXi​ 与XjX_jXj​ 相连,容量为+∞+ \infty+∞ ,费用为1,意义同上。

以以上方式建图跑最小费用最大流即可。最大流保证能够平衡货物,而最小费用流能保证运输的货物最少。

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int M=210000;int n,m,ss,tt,inf=0x7fffffff;
struct danicliu{
    int nex[M],cos[M],to[M],head[M],tot,vis[M],dis[M],flow[M],cap[M],id[M],pre[M],mflow,mcost;
    queue<int>q;
    void init(){
        tot=1;memset(head,0,sizeof head);mflow=mcost=0;
    }
    void add(int x,int y,int z,int w){
        cos[++tot]=w;
        cap[tot]=z;
        to[tot]=y;
        nex[tot]=head[x];
        head[x]=tot;
    }
    int bfs(int s,int t){

        memset(vis,0,sizeof vis);
        memset(pre,-1,sizeof pre);
        memset(dis,127,sizeof dis);inf=dis[0];
        dis[s]=0;q.push(s);vis[s]=1;flow[s]=inf;
        while(!q.empty()){
            int x=q.front();q.pop();vis[x]=0;
            for(int i=head[x],tmp;i>=2;i=nex[i]){
                if(dis[tmp=to[i]]>dis[x]+cos[i]&&cap[i]) {
                    dis[tmp]=dis[x]+cos[i];id[tmp]=i;
                    pre[tmp]=x;flow[tmp]=min(flow[x],cap[i]);
                    if(!vis[tmp]) vis[tmp]=1,q.push(tmp);
                }
            }       
        }
        return dis[t]<inf;
    }
    void maxflow(int s,int t){

        while(bfs(s,t)){
            int k=t;
            while(k!=s){
                cap[id[k]]-=flow[t];
                cap[id[k]^1]+=flow[t];
                k=pre[k];
            }
            mflow+=flow[t];
            mcost+=flow[t]*dis[t];
        }
    }
    void adx(int x,int y,int cax,int c){
    add(x,y,cax,c),add(y,x,0,-c);}
}da;int ans,val[210];
int main(){
    scanf("%d",&n);int s=0,t=2000;
    da.init();
    for(int i=1;i<=n;i++) scanf("%d",&val[i]),ans+=val[i];
    ans/=n;
    for(int i=1;i<=n;i++){
        val[i]-=ans;
        if(val[i]>0) da.add(s,i,val[i],0),da.add(i,s,0,0);
        else da.add(i+n,t,-val[i],0),da.add(t,i+n,0,0);
    }
    for(int i=1;i<=n;i++){
        if(i!=n) da.adx(i,i+1,inf,1),da.adx(i,i+n+1,inf,1);
        if(i!=1) da.adx(i,i-1,inf,1),da.adx(i,i+n-1,inf,1);
    }
    da.adx(1,n,inf,1),da.adx(1,2*n,inf,1),da.adx(n,1,inf,1),da.adx(n,n+1,inf,1);
    da.maxflow(s,t);
    printf("%d",da.mcost);
}

软件补丁问题

不知道为什么是网络流。。。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue> 
using namespace std;
const int M=510;
char s[M];
int n,m,t[M],INF,b1[M],b2[M],f1[M],f2[M],dis[1<<20+1],vis[1<<20+1];
void spfa(){
    memset(dis,127/3,sizeof dis);
    INF=dis[0];
    dis[(1<<n)-1]=0;
    queue<int>q;
    q.push((1<<n)-1);vis[(1<<n)-1]=1;
    while(!q.empty()){
        int x=q.front();q.pop();vis[x]=0;
        for(int i=1;i<=m;i++)
        if(((b1[i]&x)==b1[i])&&(!(b2[i]&x))&&(dis[(x|f1[i])&(~f2[i])]>dis[x]+t[i])){
            dis[(x|f1[i])&(~f2[i])]=dis[x]+t[i];
            if(!vis[(x|f1[i])&(~f2[i])]) q.push((x|f1[i])&(~f2[i])),vis[(x|f1[i])&(~f2[i])]=1;
        }   
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1,len;i<=m;i++){
        cin>>t[i]>>s+1;len=strlen(s+1);
        for(int j=1;j<=len;j++)
        if(s[j]=='+') b1[i]|=1<<j-1;
        else if(s[j]=='-') b2[i]|=1<<j-1;
        cin>>s+1;len=strlen(s+1);
        for(int j=1;j<=len;j++)
        if(s[j]=='+') f1[i]|=1<<j-1;
        else if(s[j]=='-') f2[i]|=1<<j-1;
    }
    spfa();

    if(dis[0]!=INF) printf("%d",dis[0]);
    else printf("0");
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值