[网络流 24 题] 餐巾计划问题

LOJ 6008
luogu1251

这道题题目描述粘上来有莫名其妙的BUG,所以就不粘了qwq

题解
这道题有更好的解决方式,耗时比我的方法快了不好,以后再去研究。
每天都要消耗一定数量的餐巾,最小费用最大流的算法会把到原点的最大流算出来,所以我们把每个代表一天的点与汇点t连一条容量为Ri,费用为0的边,这样就可以保证求出的最小费用是每天刚好用Ri条餐巾的最小费用。
每天都可以买任意数量新的餐巾,将源点s与每个代表一天的点连一条容量为INF, 费用为P的边。
每天用过的餐巾会变成脏的,而脏的餐巾可以通过洗的方法变回干净的餐巾,为了储存脏的餐巾的量,我们将每个代表一天的点分成两个点,其中一个点就是原来的点,另一个点则用来储存脏的餐巾的量的信息。
每天都会产生Ri条脏的餐巾,所以我们从源点s向每一个代表第i天脏的餐巾的点连一条容量为Ri,费用为0的边。
每天都可以洗一些餐巾,所以将第i天脏的餐巾与第i+M,i+N天连一条容量为INF,费用为F, S的边
每天也可以不洗任何餐巾,将脏的餐巾留到下一天,将第i天脏的餐巾与第i+1天脏的餐巾连一套容量为INF, 费用为0的边
这样建图就完成了。重点在于网络流算法是求出源点到汇点的最大流、最小费用,要找好源点和汇点。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5000;
const int INF = 1<<30;

int n, s, t, P, M, F, N, S, need[MAXN];
int tot=1, front[MAXN];
int ansflow=0, anscost=0, dis[MAXN], pre[MAXN], flow[MAXN];
bool inq[MAXN];

struct tEdge
{
    int v, w, next, c, f;
    inline void addEdge(int tmpu, int tmpv, int tmpc, int tmpw)
    {
        w = tmpw; v = tmpv; c = tmpc; f = 0;
        next = front[tmpu]; front[tmpu] = tot;
    }
} e[MAXN*MAXN*2];

bool spfa()
{
    queue <int> q; bool b = false;
    memset(inq, false, sizeof(inq));
    memset(flow, 0, sizeof(flow));
    memset(pre, 0, sizeof(pre));
    for(int i=1; i<=2*n+2; i++) dis[i] = INF;
    dis[s] = 0; flow[s] = INF; inq[s] = true; q.push(s);

    while(!q.empty())
    {
        int u = q.front(); q.pop(); inq[u] = false;

        for(int i=front[u]; i>0; i=e[i].next)
        {
            int v = e[i].v, w = e[i].w, maxflow = e[i].c - e[i].f;

            if(maxflow <= 0) continue;
            if(dis[v] <= dis[u] + w) continue;

            dis[v] = dis[u] + w; pre[v] = i;
            flow[v] = min(flow[u], maxflow);

            if(inq[v] == true) continue;
            inq[v] = true;
            q.push(v);
            if(v == t) b = true;
        }
    }

    return b;
}

void kp()
{
    while(spfa() == true)
    {
        int k = t, curflow = flow[t], curcost = dis[t];

        while(k != s)
        {
            e[pre[k]].f += curflow;
            e[pre[k]^1].f -= curflow;
            k = e[pre[k]^1].v;
        }

        ansflow += curflow;
        anscost += curflow * curcost;
    }
}

int main()
{
    scanf("%d%d%d%d%d%d", &n, &P, &M, &F, &N, &S);
    s = n*2+1; t = n*2+2; //1~n:每天的新纸巾 n+1~2n:每天的旧纸巾

    for(int i=1; i<=n; i++) scanf("%d", &need[i]);
    for(int i=1; i<=n; i++)
    {
        e[++tot].addEdge(s, i, INF, P);
        e[++tot].addEdge(i, s, 0, -P);
        e[++tot].addEdge(i, t, need[i], 0);
        e[++tot].addEdge(t, i, 0, 0);

        e[++tot].addEdge(s, i+n, need[i], 0);
        e[++tot].addEdge(i+n, s, 0, 0);

        if(i+1 <= n) 
        {
            e[++tot].addEdge(i+n, i+n+1, INF, 0);
            e[++tot].addEdge(i+n+1, i+n, 0, 0);
        }
        if(i+M <= n)
        {
            e[++tot].addEdge(i+n, i+M, INF, F);
            e[++tot].addEdge(i+M, i+n, 0, -F);
        }
        if(i+N <= n)
        {
            e[++tot].addEdge(i+n, i+N, INF, S);
            e[++tot].addEdge(i+N, i+n, 0, -S);
        }
    }

    kp();

    printf("%d\n", anscost);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值