BZOJ4380 [POI2015]Myjnie

187 篇文章 0 订阅
99 篇文章 0 订阅

标签:区间DP

题目

题目传送门

Description

有n家洗车店从左往右排成一排,每家店都有一个正整数价格p[i]。

有m个人要来消费,第i个人会驶过第a[i]个开始一直到第b[i]个洗车店,且会选择这些店中最便宜的一个进行一次消费。

但是如果这个最便宜的价格大于c[i],那么这个人就不洗车了。

请给每家店指定一个价格,使得所有人花的钱的总和最大。

Input

第一行包含两个正整数 n,m(1n501m4000) n , m ( 1 ≤ n ≤ 50 , 1 ≤ m ≤ 4000 )

接下来m行,每行包含三个正整数 a[i],b[i],c[i](1a[i]b[i]n1c[i]500000) a [ i ] , b [ i ] , c [ i ] ( 1 ≤ a [ i ] ≤ b [ i ] ≤ n , 1 ≤ c [ i ] ≤ 500000 )

Output

第一行输出一个正整数,即消费总额的最大值。

第二行输出n个正整数,依次表示每家洗车店的价格 p[i] p [ i ] ,要求 1p[i]500000 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

分析

首先将 c c 离散化,显然p[i]一定存在于 c c

设状态g[i][j][k]表示区间 i>j i − > j 中所有花费 k ≥ k 的最大收益

h[x][k] h [ x ] [ k ] 表示经过x点的 cik c i ≥ k 的人数

f[i][j][k] f [ i ] [ j ] [ k ] 为区间[i,j]最小值为k的最优方案(仅仅记录下来)

和区间DP转移类似,枚举分割点x

g[i][j][k]=g[i][l1][k]+g[l+1][j][k]+v[k]h[x][k] g [ i ] [ j ] [ k ] = g [ i ] [ l − 1 ] [ k ] + g [ l + 1 ] [ j ] [ k ] + v [ k ] ∗ h [ x ] [ k ]

记录方案后通过dfs输出

code

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
#define reg(x) for(int i=last[x];i;i=e[i].next)
using namespace std;
inline ll read(){
    ll f=1,x=0;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;
}
const int maxn=56,maxm=4e3+6;
int n,m,a[maxm],b[maxm],c[maxm],v[maxm],h[maxn][maxm];
char f[maxn][maxn][maxm];int g[maxn][maxn][maxm],p[maxn][maxn][maxm];
void dfs(int l,int r,int k){
    if(l>r)return;
    int x=f[l][r][k=p[l][r][k]];
    a[x]=v[k],dfs(l,x-1,k),dfs(x+1,r,k);
}
inline int lower(int x){
    int l=1,r=m,mid,tmp;
    while(l<=r)if(v[mid=(l+r)>>1]<=x)l=(tmp=mid)+1;else r=mid-1;
    return tmp;
}
int main(){
    n=read(),m=read();
    rep(i,1,m)a[i]=read(),b[i]=read(),v[i]=c[i]=read();
    sort(v+1,v+1+m);
    rep(i,1,m)c[i]=lower(c[i]);
    dep(i,n,1)
        rep(j,i,n){
            rep(k,i,j)rep(x,1,m)h[k][x]=0;
            mem(h,0);
            rep(k,1,m)if(i<=a[k]&&b[k]<=j)rep(x,a[k],b[k])h[x][c[k]]++;
            rep(k,i,j)dep(x,m-1,1)h[k][x]+=h[k][x+1];
            dep(k,m,1){
                int x,y;
                for(x=i,y=0;x<=j;x++){
                    int tmp=g[i][x-1][k]+g[x+1][j][k]+v[k]*h[x][k];
                    if(tmp>=y)y=tmp,f[i][j][k]=x;
                }
                if(y>=g[i][j][k+1])g[i][j][k]=y,p[i][j][k]=k;
                else g[i][j][k]=g[i][j][k+1],p[i][j][k]=p[i][j][k+1];
            }
        }
    dfs(1,n,1);
    printf("%d\n",g[1][n][1]);
    rep(i,1,n)printf("%d ",a[i]);puts("");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值