【JZOJ4931】A
Description
有N家洗车店从左往右排成一排,每家店都有一个正整数价格Pi。
有M个人要来消费,第i个人会驶过第Ai个开始一直到第Bi个洗车店,且会选择这些店中最便宜的一个进行一次消费。但是如果这个最便宜的价格大于Ci,那么这个人就不洗车了。
请给每家店指定一个价格,使得所有人花的钱的总和最大。
Solution
先把数离散化。
考虑区间dp,设 fl,r,x 表示区间 [l,r] 中最小的数为x。
枚举中间的一个位置
p∈[l,r]
,代表当前花费最小的位置,那么有
fl,r,x=max(fl,r,x,fl,p−1,x+fp+1,r,x+奇怪的东西)
我们考虑怎么求这个奇怪的东西(也就是跨越中间p的贡献),那我们显然要知道 [l,r] 有多少人是可以接受 x 的。
设这个人数为
那么总的方程:
fl,r,x=max(fl,r,x,fl,p−1,x+fp+1,r,x+tot⋅x)
因为我们
f
求的是以
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define max(a,b) ((a)>(b))?(a):(b)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 55
#define M 4010
#define MAX 500010
using namespace std;
int a[M],b[M],c[M],cc[M];
int ls[MAX],yl[M],tt=0;
int f[N][N][M],g[N][M];
int main()
{
int n,m;
scanf("%d %d",&n,&m);
fo(i,1,m) scanf("%d %d %d",&a[i],&b[i],&c[i]),cc[i]=c[i];
sort(cc+1,cc+m+1);
fo(i,1,m)
{
ls[cc[i]]=++tt;
yl[tt]=cc[i];
}
fo(len,1,n)
fo(l,1,n-len+1)
{
int r=l+len-1;
memset(g,0,sizeof(g));
fo(i,1,m) if(a[i]>=l && b[i]<=r)
fo(j,a[i],b[i]) g[j][ls[c[i]]]++;
fo(i,l,r)
fd(j,tt-1,1) g[i][j]+=g[i][j+1];
fo(i,l,r)
fd(x,tt,1)
f[l][r][x]=max(max(f[l][r][x],f[l][r][x+1]),f[l][i-1][x]+f[i+1][r][x]+g[i][x]*yl[x]);
}
printf("%d",f[1][n][1]);
}