https://www.luogu.org/problemnew/show/P1251
题目描述
一个餐厅在相继的 NNN 天里,每天需用的餐巾数不尽相同。假设第 iii 天需要 rir_iri块餐巾( i=1,2,...,N)。餐厅可以购买新的餐巾,每块餐巾的费用为 ppp 分;或者把旧餐巾送到快洗部,洗一块需 m 天,其费用为 f 分;或者送到慢洗部,洗一块需 nnn 天(n>mn>mn>m),其费用为 sss 分(s<fs<fs<f)。
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好 NNN 天中餐巾使用计划,使总的花费最小。编程找出一个最佳餐巾使用计划。
输入输出格式
输入格式:
由标准输入提供输入数据。文件第 1 行有 1 个正整数 NNN,代表要安排餐巾使用计划的天数。
接下来的 NNN 行是餐厅在相继的 NNN 天里,每天需用的餐巾数。
最后一行包含5个正整数p,m,f,n,sp,m,f,n,sp,m,f,n,s。ppp 是每块新餐巾的费用; mmm 是快洗部洗一块餐巾需用天数; fff 是快洗部洗一块餐巾需要的费用; nnn 是慢洗部洗一块餐巾需用天数; sss 是慢洗部洗一块餐巾需要的费用。
输出格式:
将餐厅在相继的 N 天里使用餐巾的最小总花费输出
输入输出样例
输入样例#1: 复制
3
1 7 5
11 2 2 3 1
输出样例#1: 复制
134
说明
N<=2000
ri<=10000000
p,f,s<=10000
时限4s
思路:
首先,我们拆点,将一天拆成晚上和早上,每天晚上会受到脏餐巾(来源:当天早上用完的餐巾,在这道题中可理解为从原点获得),每天早上又有干净的餐巾(来源:购买、快洗店、慢洗店)。
1.从原点向每一天晚上连一条流量为当天所用餐巾x,费用为0的边,表示每天晚上从起点获得x条脏餐巾。
2.从每一天早上向汇点连一条流量为当天所用餐巾x,费用为0的边,每天白天,表示向汇点提供x条干净的餐巾,流满时表示第i天的餐巾够用 。 3.从每一天晚上向第二天晚上连一条流量为INF,费用为0的边,表示每天晚上可以将脏餐巾留到第二天晚上(注意不是早上,因为脏餐巾在早上不可以使用)。
4.从每一天晚上向这一天+快洗所用天数t1的那一天早上连一条流量为INF,费用为快洗所用钱数的边,表示每天晚上可以送去快洗部,在地i+t1天早上收到餐巾 。
5.同理,从每一天晚上向这一天+慢洗所用天数t2的那一天早上连一条流量为INF,费用为慢洗所用钱数的边,表示每天晚上可以送去慢洗部,在地i+t2天早上收到餐巾 。
6.从起点向每一天早上连一条流量为INF,费用为购买餐巾所用钱数的边,表示每天早上可以购买餐巾 。 注意,以上6点需要建反向边!3~6点需要做判断(即连向的边必须<=n)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const ll INF=1e16;
const int maxn=5005;
const int maxm=50005;
struct Edge
{
int to,nxt;
ll f,w;
}edge[maxm<<1];
int n,m,s,t,tot;
int head[maxn];
int prevx[maxn],pree[maxn],inque[maxn];
ll dist[maxn];
queue<int> que;
inline void addedge(int u,int v,ll c,ll w)//容量为c 权值为w
{
edge[++tot].to=v,edge[tot].f=c,edge[tot].w=w,edge[tot].nxt=head[u],head[u]=tot;
edge[++tot].to=u,edge[tot].f=0,edge[tot].w=-w,edge[tot].nxt=head[v],head[v]=tot;
}
bool findpath()
{
while(!que.empty())
que.pop();
que.push(s);
fill(dist,dist+t+1,INF);
dist[s]=0;
inque[s]=1;
while(!que.empty())
{
int u=que.front();
que.pop();
for(int i=head[u];i;i=edge[i].nxt)
{
if(edge[i].f>0&&dist[u]+edge[i].w<dist[edge[i].to])
{
dist[edge[i].to]=dist[u]+edge[i].w;
prevx[edge[i].to]=u;
pree[edge[i].to]=i;
if(!inque[edge[i].to])
{
inque[edge[i].to]=1;
que.push(edge[i].to);
}
}
}
inque[u]=0;
}
if(dist[t]<INF)
return 1;
return 0;
}
void mincostfolw()
{
ll ans=0;
while(findpath())
{
int u=t;
ll delta=INF;
while(u!=s)
{
if(edge[pree[u]].f<delta)
delta=edge[pree[u]].f;
u=prevx[u];
}
u=t;
while(u!=s)
{
edge[pree[u]].f-=delta;
edge[pree[u]^1].f+=delta;
u=prevx[u];
}
ans+=dist[t]*delta;
}
printf("%lld\n",ans);
}
int main()
{
tot=1;
scanf("%d",&n);
s=0,t=2*n+1;
int c;
for(int i=1;i<=n;i++)
{
scanf("%d",&c);
addedge(s,i,c,0);
addedge(i+n,t,c,0);
}
int p,m,f,n1,s1;
scanf("%d%d%d%d%d",&p,&m,&f,&n1,&s1);
for(int i=1;i<=n;i++)
{
addedge(s,i+n,INF,p);
if(i+1<=n)
addedge(i,i+1,INF,0);
if(i+m<=n)
addedge(i,i+m+n,INF,f);
if(i+n1<=n)
addedge(i,i+n1+n,INF,s1);
}
mincostfolw();
return 0;
}