Description
有n家洗车店从左往右排成一排,每家店都有一个正整数价格p[i]。
有m个人要来消费,第i个人会驶过第a[i]个开始一直到第b[i]个洗车店,且会选择这些店中最便宜的一个进行一次消费。但是如果这个最便宜的价格大于c[i],那么这个人就不洗车了。
请给每家店指定一个价格,使得所有人花的钱的总和最大。
Input
第一行包含两个正整数n,m(1<=n<=50,1<=m<=4000)。
接下来m行,每行包含三个正整数a[i],b[i],ci
Output
第一行输出一个正整数,即消费总额的最大值。
第二行输出n个正整数,依次表示每家洗车店的价格p[i],要求1<=p[i]<=500000。
若有多组最优解,输出任意一组。
Sample Input
7 5
1 4 7
3 7 13
5 6 20
6 7 1
1 2 5
Sample Output
43
5 5 13 13 20 20 13
解题思路:
由贪心可知,最后的p[i]肯定是某个c[i]。
首先将 c 离散化,考虑区间 DP,设 f(i,j,k) 为区间 [i,j] 最小值为 k 时的最大收益(只考虑i<=a<=b<=j的人)。
转移时枚举最小值所在位置 x,那么可以用f(i,x–1,≥k)+f(x+1,j,≥k)+cnt[x][k]*k 来更新 f(i,j,k),cnt[x][k]为考虑的人中经过x且c[i]≥k的人数,他们肯定都会选k。
时间复杂度 O(n^3m),要输出方案则记录决策点即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')c=getchar(),f=-1;
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=55,M=4005;
int n,m,mx,a[M],b[M],c[M],val[M],ans[N],f[N][N][M],cnt[N][M],g[N][N][M],p[N][N][M];
void get_ans(int l,int r,int k)
{
if(l>r)return;
int i=p[l][r][k],j=g[l][r][k];
ans[i]=val[j];
get_ans(l,i-1,j),get_ans(i+1,r,j);
}
int main()
{
//freopen("lx.in","r",stdin);
n=getint(),m=getint();
for(int i=1;i<=m;i++)a[i]=getint(),b[i]=getint(),c[i]=val[i]=getint();
sort(val+1,val+m+1),mx=unique(val+1,val+m+1)-val-1;
for(int i=1;i<=m;i++)c[i]=lower_bound(val+1,val+mx+1,c[i])-val;
for(int l=n;l;l--)for(int r=l;r<=n;r++)
{
for(int i=l;i<=r;i++)for(int k=1;k<=mx;k++)cnt[i][k]=0;
for(int i=1;i<=m;i++)if(l<=a[i]&&b[i]<=r)
for(int j=a[i];j<=b[i];j++)cnt[j][c[i]]++;
for(int i=l;i<=r;i++)for(int k=mx;k;k--)cnt[i][k]+=cnt[i][k+1];
for(int k=mx;k;k--)
{
p[l][r][k]=l,g[l][r][k]=k;
for(int i=l;i<=r;i++)
{
int tmp=f[l][i-1][k]+f[i+1][r][k]+cnt[i][k]*val[k];
if(tmp>f[l][r][k])f[l][r][k]=tmp,p[l][r][k]=i;
}
if(f[l][r][k+1]>f[l][r][k])f[l][r][k]=f[l][r][k+1],g[l][r][k]=g[l][r][k+1],p[l][r][k]=p[l][r][k+1];
}
}
cout<<f[1][n][1]<<'\n';
get_ans(1,n,1);
for(int i=1;i<=n;i++)cout<<ans[i]<<' ';
return 0;
}