洛谷 P1251 餐巾计划问题 网络流 费用流 Dinic+Spfa

题目连接:

https://www.luogu.com.cn/problem/P1251

参考博客:

https://www.luogu.com.cn/blog/user31955/solution-p1251

思路理的非常棒,这个博客,建议复习再看

图解:

思路:

 1:首先,我们拆点,将一天拆成晚上和早上,每天晚上会收到脏餐巾(来源:当天早上用完的餐巾,在这道题中可理解为从原点获得),每天早上又有干净的餐巾(来源:购买、快洗店、慢洗店)

2:在这道题目的思路解释中,你可以把T点当作这个饭店的早上,T之后就是这一天就餐中,然后就到了S点,就是到了晚上,T和S是饭店的早晚两种状态,T状态时,是早上,接受干净的餐巾纸,S状态时,是晚上,接收这一天弄脏的餐巾纸

作图:

1:从原点向每一天晚上连一条流量为当天所用餐巾x,费用为0的边,表示每天晚上从起点获得x条脏餐巾

2:从每一天早上向汇点连一条流量为当天所用餐巾x,费用为0的边,每天白天,表示向汇点提供x条干净的餐巾,流满时表示第i天的餐巾够用

3:从每一天晚上向第二天晚上连一条流量为INF,费用为0的边,表示每天晚上可以将脏餐巾留到第二天晚上(注意不是早上,因为脏餐巾在早上不可以使用

4:从每一天晚上向这一天+快洗所用天数t1的那一天早上连一条流量为INF,费用为快洗所用钱数的边,表示每天晚上可以送去快洗部,在地i+t1天早上收到餐巾

5:同理,从每一天晚上向这一天+慢洗所用天数t2的那一天早上连一条流量为INF,费用为慢洗所用钱数的边,表示每天晚上可以送去慢洗部,在地i+t2天早上收到餐巾

6:.从起点向每一天早上连一条流量为INF,费用为购买餐巾所用钱数的边,表示每天早上可以购买餐巾 。

7:注意,以上6点需要建反向边!3~5点需要做判断(即连向的边必须<=n)

一:代码

#include <bits/stdc++.h>

using namespace std;
//const int maxn=1e5+1,maxm=1e5+1,inf=1<<31-1;
const int maxn=4e3+2,maxm=3e4+1,inf=1<<31-1;
int n,s,t,a,p,m,f,n1,s1,tot=1,head[maxn],flow[maxn],pre[maxn],last[maxn],maxflow;
//dis最小花费;pre每个点的前驱;last每个点的所连的前一条边;flow源点到此处的流量
bool vis[maxn];
long long dis[maxn],mincost;
queue<int>q;

struct edge
{
    int to,next,w,dis;//w流量 dis花费
}e[maxm];

void addedge(int x,int y,int w,int dis)
{
    e[++tot].to=y;
    e[tot].w=w;
    e[tot].dis=dis;
    e[tot].next=head[x];
    head[x]=tot;
}

bool spfa(int s,int t)
{
    memset(dis,0x7f,sizeof(dis));
    memset(flow,0x7f,sizeof(flow));
    memset(vis,0,sizeof(vis));
    q.push(s);vis[s]=1;dis[s]=0;pre[t]=-1;
    while(!q.empty())
    {
        int now=q.front();q.pop();
        vis[now]=0;
        for(int i=head[now];i!=-1;i=e[i].next)
        {
            int y=e[i].to;
            if(e[i].w>0&&dis[y]>dis[now]+e[i].dis)//相当于求最短路
            {
                dis[y]=dis[now]+e[i].dis;
                pre[y]=now;
                last[y]=i;
                flow[y]=min(flow[now],e[i].w);
                if(!vis[y])
                {
                    vis[y]=1;q.push(y);
                }
            }
        }
    }
    return pre[t]!=-1;
}

void dfs()
{
    int now=t;
    maxflow+=flow[t];
    mincost+=flow[t]*dis[t];
    while(now!=s)//从汇点一直回溯到源点
    {
        e[last[now]].w-=flow[t];
        e[last[now]^1].w+=flow[t];
        now=pre[now];
    }
}

int main()
{
    ios::sync_with_stdio(0);
    scanf("%d",&n);
    s=0,t=2*n+1;
    memset(head,-1,sizeof(head));//初始化
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a);
        addedge(s,i,a,0),addedge(i,s,0,0);
        addedge(i+n,t,a,0),addedge(t,i+n,0,0);
        if(i!=n)addedge(i,i+1,inf,0),addedge(i+1,i,0,0);
    }
    scanf("%d %d %d %d %d",&p,&m,&f,&n1,&s1);
    for(int i=1;i<=n;i++)
    {
        addedge(s,i+n,inf,p),addedge(i+n,s,0,-p);
        if(i+m<=n)addedge(i,i+n+m,inf,f),addedge(i+n+m,i,0,-f);
        if(i+n1<=n)addedge(i,i+n+n1,inf,s1),addedge(i+n+n1,i,0,-s1);
    }
    while(spfa(s,t))dfs();
    printf("%lld\n",mincost);
    return 0;
}
发布了164 篇原创文章 · 获赞 82 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/aiwo1376301646/article/details/104303659