bzoj4380: [POI2015]Myjnie【區間dp】

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章