luogu P1251 餐巾计划问题

题目链接

luogu P1251 餐巾计划问题

题解

考虑上下界网络流
对于没个时间点拆成两个点\(A,A'\)
由S点连向\(A\)限制最小流入量,处理这部分流量时相当于计算花费
然后就是考虑如和相依状态转移,建边
不要忘了剩下干净的

代码

// luogu-judger-enable-o2
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using std::queue;
using std::min;
#define INF 0x7fffffff
const int maxn = 770000;
inline int read() {
    int x=0;char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c<='9'&&c>='0') x=x*10+c-'0',c=getchar();
    return x;
}
int N,S,T,ned[maxn],p,m,f,n,s;
struct node{
    int u,v,flow,next,cost;
}edge[maxn];int head[maxn],num=1;
inline void add_edge(int u,int v,int flow,int cost) {
    edge[++num].v=v;edge[num].u=u,edge[num].flow=flow,edge[num].next=head[u];head[u]=num;
    edge[num].cost=cost;
    edge[++num].v=u;edge[num].u=v,edge[num].flow=0;edge[num].next=head[v];head[v]=num;
    edge[num].cost=-cost;
}
int Min_cost=0;
int pre[maxn],dis[maxn];
bool vis[maxn];
bool spfa() {
    memset(dis,0x3f,sizeof dis);
    memset(vis,0,sizeof vis);
    queue<int>que;
    que.push(S),vis[S]=1;dis[S]=0;
    while(!que.empty()) {
        int u=que.front();que.pop();
        for(int i=head[u];i;i=edge[i].next) {
            int v=edge[i].v;
            if(edge[i].flow && dis[v]>dis[u]+edge[i].cost) {
                dis[v]=dis[u]+edge[i].cost;
                pre[v]=i;
                if(!vis[v]) que.push(v),vis[v]=1;
            }
        }
        vis[u]=0;
    }
    if(dis[T]!=0x3f3f3f3f)return true;
    else return false;
}
int calc() {
    int ret=0,maxx=0x3f3f3f3f;
    for(int i=T;i!=S;i=edge[pre[i]].u) 
        maxx=min(edge[pre[i]].flow,maxx);
    for(int i=T;i!=S;i=edge[pre[i]].u) {
        edge[pre[i]].flow-=maxx;
        edge[pre[i]^1].flow+=maxx;
        ret+=maxx*edge[pre[i]].cost;
    }
    return ret;
}
long long ans=0;
void mcmf() {
    while(spfa()) 
        ans+=calc();
}
int main() {
    N=read();
    for(int i=1;i<=N;++i) ned[i]=read();
    p=read(),m=read(),f=read(),n=read(),s=read();
    T=N*2+1;
    for(int i=1;i<=N;++i) {
        add_edge(S,i,ned[i],0);
        add_edge(N+i,T,ned[i],0);
    }
    for(int i=1;i<=N;++i) {
        if(i+1<=N)add_edge(i,i+1,INF,0);
        if(i+m<=N)add_edge(i,N+i+m,INF,f);
        if(i+n<=N)add_edge(i,N+i+n,INF,s);
        add_edge(S,i+N,INF,p);
    }
    mcmf();
    printf("%lld\n",ans);
    return 0;
}   

转载于:https://www.cnblogs.com/sssy/p/8429977.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值