餐巾计划问题

本文介绍如何运用二分图和最小费用最大流解决餐巾计划问题。关键在于将当天需求与剩余分开考虑,建立源点、汇点及各节点间的连接,并设置合适的流量与花费。通过最小费用最大流算法求解,但要注意数据范围可能导致错误。
摘要由CSDN通过智能技术生成

关键是要想到建立二分图,这个确实一开始没想到。
重点是把当天需要的和当天剩下的给分开。这样就可以建图了。
1.对于当天需要的,跟源点连边,流量为 ri ,花费为p。跟汇点连边,花费为0,流量为 ri
2.对于当天剩下的,跟源点相连,流量为 ri ,因为当天的肯定会剩下的,然后对于第i天,向第i+1天连边,花费0,流量为inf。因为当天的可以放到第二天。然后对于第i天剩下的,可以向第i+day天的需要的点连边,表示洗好的。花费为相应的店的花费,流量为inf。
然后跑最小费用最大流就可以了。
因为数据范围的问题,wa了好几发QAQ。

#include <bits/stdc++.h>

using namespace std;
const int MAXN=10000+5;
const int inf=1e9+7;
int n,p,day1,cost1,day2,cost2;
int s,e;

int cnt,head[MAXN];
struct node
{
    int u,v,w,f,next;
} edge[1002005];

void init()
{
    cnt=0;
    for(int i=0; i<=e; ++i)
    {
        head[i]=-1;
    }
}

void add(int u,int v,int w,int f)
{
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].f=f;
    edge[cnt].next=head[u];
    head[u]=cnt++;
    edge[cnt].u=v;
    edge[cnt].v=u;
    edge[cnt].w=-w;
    edge[cnt].f=0;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}

bool vis[MAXN];
int dis[MAXN],pre[MAXN];
bool spfa()
{
    int i;
    for(i = 0; i <= e; ++i)
    {
        dis[i]=inf;
        pre[i]=-1;
    }
    queue<int>q;
    q.push(s);
    dis[s]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(i=head[u]; i!=-1; i=edge[i].next)
        {
            int v=edge[i].v;
            int w=edge[i].w;
            int f=edge[i].f;
            if(f>0&&dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                pre[v]=i;
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    if(pre[e]==-1)return 0;
    return 1;
}
int get_mincost()
{
    int min_cost = 0,max_flow=0;
    while(spfa())
    {
        int p=pre[e];
        int flow=inf;
        while(p!=-1)
        {
            flow=min(flow,edge[p].f);
            p=pre[edge[p].u];
        }
        max_flow+=flow;
        min_cost += flow*dis[e];
        p=pre[e];
        while(p!=-1)
        {
            edge[p].f-=flow;
            edge[p^1].f+=flow;
            p=pre[edge[p].u];
        }
    }
    return min_cost;
}

int need[MAXN];


int main()
{
    scanf("%d%d%d%d%d%d",&n,&p,&day1,&cost1,&day2,&cost2);
    for(int i = 1; i <= n; ++i)scanf("%d",&need[i]);
    s = 0, e = n<<1|1;
    init();
    for(int i = 1; i <= n; ++i)
    {
        add(s,i,0,need[i]);
        add(i+n,e,0,need[i]);
        add(s,i+n,p,need[i]);
    }
    for(int i = 2; i <= n; ++i)add(i-1,i,0,inf);
    //快洗
    for(int i = 1; i <= n-day1; ++i)add(i,i+day1+n,cost1,inf);
    //慢洗
    for(int i = 1; i <= n-day2; ++i)add(i,i+day2+n,cost2,inf);
    printf("%d\n",get_mincost());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值