【BZOJ-4380】Myjnie 区间DP

4380: [POI2015]Myjnie

Time Limit: 40 Sec  Memory Limit: 256 MBSec  Special Judge
Submit: 162  Solved: 82
[Submit][Status][Discuss]

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

HINT

Source

鸣谢Claris

Solution

这个区间dp好厉害啊,自己的转移并没有想到,最后是看着Claris的课件做的。

现将$c_{i}$离散化,然后区间dp

$dp[l][r][p]$表示区间$[l,r]$,最小价值为$p$的最大总和,$cnt[k][c]$表示经过$k$位置的费用$\geqslant c$的数量

转移时通过枚举最小值所在的位置$k$,来进行转移$dp[l][r][p]<--dp[l][k-1][p]+dp[k+1][r][p]+cost(k)$

还要记录方案,最后用dfs输出。

总的时间复杂度$O(N^{3}M)$

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int N,M,a[5050],b[5050],c[5050],C[5050],price[5050],ls[5050],tp,top;
int from[55][55][5050],last[55][55][5050],cnt[55][5050],dp[55][55][5050];
inline void Get(int l,int r,int p)
{
    if (l>r) return;
    int fr=from[l][r][p],la=last[l][r][p];
//    printf("%d  %d  %d  %d\n",l,r,p,fr);
    price[fr]=C[la];
    Get(l,fr-1,la); Get(fr+1,r,la);
}
int main()
{
    N=read(),M=read();
    for (int i=1; i<=M; i++) a[i]=read(),b[i]=read(),C[i]=c[i]=read(),ls[++tp]=c[i];
    sort(ls+1,ls+tp+1);
    for (int i=1; i<=tp; i++) if (ls[top]!=ls[i]) ls[++top]=ls[i];
    for (int i=1,x; i<=M; i++) x=lower_bound(ls+1,ls+top+1,c[i])-ls,C[x]=c[i],c[i]=x;
//    for (int i=1; i<=M; i++) printf("%d  ",C[i]); puts("");
    for (int l=1; l<=N; l++)
        for (int r=l; r<=N; r++)
            for (int p=1; p<=M; p++)
                dp[l][r][p]=-0x3fffffff;
    for (int len=1; len<=N; len++)
        for (int l=1,r=l+len-1; r<=N; l++,r++)
            {
                for (int k=l; k<=r; k++)
                    for (int i=1; i<=M; i++)
                        cnt[k][i]=0;
                for (int i=1; i<=M; i++)
                    if (a[i]>=l && b[i]<=r)
                        for (int k=a[i]; k<=b[i]; k++)
                            cnt[k][c[i]]++;
                for (int k=l; k<=r; k++)
                    for (int i=M-1; i; i--)
                        cnt[k][i]+=cnt[k][i+1];
                for (int k=l; k<=r; k++)
                    for (int i=1; i<=M; i++)
                        if (dp[l][k-1][i]+dp[k+1][r][i]+C[i]*cnt[k][i]>dp[l][r][i])
                            dp[l][r][i]=dp[l][k-1][i]+dp[k+1][r][i]+C[i]*cnt[k][i],
                            from[l][r][i]=k,last[l][r][i]=i;// from[l][r][i]=i;
                for (int i=M-1; i; i--)
                    if (dp[l][r][i]<dp[l][r][i+1])
                        dp[l][r][i]=dp[l][r][i+1],
                        from[l][r][i]=from[l][r][i+1],last[l][r][i]=last[l][r][i+1];
//                printf("<%d , %d> == %d\n",l,r,dp[l][r][1]);
            }
    printf("%d\n",dp[1][N][1]);
    Get(1,N,1);
    for (int i=1; i<=N; i++) printf("%d ",price[i]);
    return 0;
}

 

posted @ 2016-11-10 18:59  DaD3zZ  阅读(659)  评论(0编辑  收藏  举报