【网络流24题】餐巾计划(最小费用流)

传送门

    餐巾计划

I think

    最小费用最大流。增设源汇点S,T,每i天分两个点xi,yi,分别表示每天应有餐巾与最终解决的餐巾数。记 (x,y,u,v) 表示x连向y点容量为u费用为v点的边,连接以下边 (S,xi,ai,0),(yi,T,ai,0),(xi,xi+1,Inf,0),(xi,yi+qk,Inf,qc),(xi,yi+lk,Inf,lc); 最后用SPFA+增广路思想求出最小费用即可。
    另算法函数名很无赖地用了SPFA。

Code

#include
#include
using namespace std;

const int sm = 2200;
const int sn = 12200;
const int Inf = 0x3f3f3f3f;

int N,nw,qk,qc,lw,lc,Ans,tot=1,S,T;
int to[sn],nxt[sn],hd[sm],c[sn],f[sn];
int pre[sm],vis[sm],cst[sm];

int Min(int x,int y) { return xvoid Add(int u,int v,int w,int val) {
    to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot,c[tot]=w,f[tot]=val;
    to[++tot]=u,nxt[tot]=hd[v],hd[v]=tot,c[tot]=0,f[tot]=-val;
}

void SPFA() {
    int t,df; queue<int>q;
    while(1) {
        for(int i=1;i<=T;++i) 
            pre[i]=vis[i]=0,cst[i]=Inf;
        cst[S]=0,vis[S]=1,q.push(S);
        while(!q.empty()) {
            t=q.front(),q.pop();
            vis[t]=0;
            for(int i=hd[t];i;i=nxt[i])
                if(cst[to[i]]>cst[t]+f[i]&&c[i]>0) {
                    cst[to[i]]=cst[t]+f[i];
                    pre[to[i]]=i;
                    if(!vis[to[i]]) {
                        vis[to[i]]=1;
                        q.push(to[i]);
                    }
                }
        }
        if(cst[T]==Inf)break;
        df=Inf;
        for(int i=T;i!=S;i=to[pre[i]^1]) 
            df=Min(df,c[pre[i]]);
        for(int i=T;i!=S;i=to[pre[i]^1]) 
            c[pre[i]]-=df,c[pre[i]^1]+=df;
        Ans+=df*cst[T];
    }
    printf("%d\n",Ans); 
}

int main() {
    int u;
    scanf("%d%d%d%d%d%d",&N,&nw,&qk,&qc,&lw,&lc);
    S=N<<1|1,T=S+1;
    for(int i=1;i<=N;++i) {
        scanf("%d",&u);
        Add(S,i,u,0),Add(i+N,T,u,0);
        Add(S,i+N,Inf,nw);
        if(i+1<=N) Add(i,i+1,Inf,0);
        if(i+qk<=N) Add(i,i+N+qk,Inf,qc);
        if(i+lw<=N) Add(i,i+N+lw,Inf,lc);
    }
    SPFA();
    return 0;
}

你可能感兴趣的:(网络流,费用流)