BZOJ4380[POI2015] Myjnie
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],c[i](1<=a[i]<=b[i]<=n,1<=c[i]<=500000)
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
Solution:
神一样的dp…
首先先对c[i]离散。
因为与这道题相关的量是区间的最小值,所以定义
dp[i][j][k]
表示区间
[i,j]
的最小值为
k
的最大值。再将这个dp定义扩展一下:
所以再记一个
pre[i][j][k]
表示当前的
dp[i][j][k]
的”实际
k
值“,记一个
转移时:每一次用
O(nm)
算一个当最小值为
k
时,最小值在
dp[i][j][k]=max(cnt[pos][k]+dp[i][pos−1][k]+dp[pos+1][j][k])(pos∈[i,j])
dp[i][j][k]=max(dp[i][j][k],dp[i][j][k+1])(k=Max→0)
然后再根据 pre 数组和 fa 数组还原方案即可,复杂度 O(n3m)
#include<stdio.h>
#include<iostream>
#include<algorithm>
#define N 55
#define M 4005
using namespace std;
void Rd(int &res){
char c;res=0;
while(c=getchar(),!isdigit(c));
do{
res=(res<<1)+(res<<3)+(c^48);
}while(c=getchar(),isdigit(c));
}
struct Node{int L,R,w;}A[N];
int dp[N][N][M],pre[N][N][M],fa[N][N][M],Ans[N],val[M],cnt[N][M];
void Pf(int L,int R,int k){
k=pre[L][R][k];
int pos=fa[L][R][k];
Ans[pos]=val[k];
if(pos>L)Pf(L,pos-1,k);
if(pos<R)Pf(pos+1,R,k);
}
int main(){
int n,m;
Rd(n);Rd(m);
for(int i=1;i<=m;i++){
Rd(A[i].L);Rd(A[i].R);Rd(A[i].w);
val[i-1]=A[i].w;
}
sort(val,val+m);
int sz=unique(val,val+m)-val;
for(int i=1;i<=m;i++)
A[i].w=lower_bound(val,val+sz,A[i].w)-val;
sz--;//[0,sz]
for(int L=n;L>=1;L--)
for(int R=L;R<=n;R++){
for(int i=L;i<=R;i++)
for(int j=0;j<=sz;j++)
cnt[i][j]=0;
for(int i=1;i<=m;i++){
if(A[i].L<L||A[i].R>R)continue;
for(int j=A[i].L;j<=A[i].R;j++)
cnt[j][A[i].w]++;
}
for(int i=L;i<=R;i++)
for(int j=sz-1;j>=0;j--)
cnt[i][j]+=cnt[i][j+1];
pre[L][R][sz]=sz;
for(int k=sz;k>=0;k--){
int mx=-1,pos;
for(int i=L;i<=R;i++){
int tmp1=0,tmp2=0;
if(i>L)tmp1=dp[L][i-1][k];
if(i<R)tmp2=dp[i+1][R][k];
int rs=val[k]*cnt[i][k]+tmp1+tmp2;
if(rs>mx)mx=rs,pos=i;
}
dp[L][R][k]=mx;
fa[L][R][k]=pos;
if(k<sz){
if(dp[L][R][k+1]>dp[L][R][k]){
dp[L][R][k]=dp[L][R][k+1];
pre[L][R][k]=pre[L][R][k+1];
}else pre[L][R][k]=k;
}
}
}
printf("%d\n",dp[1][n][0]);
Pf(1,n,0);
for(int i=1;i<=n;i++)
printf("%d%c",Ans[i]," \n"[i==n]);
return 0;
}