首页 > 其他分享 >P3592 [POI2015] MYJ

P3592 [POI2015] MYJ

时间:2023-05-01 20:00:38浏览次数:47  
标签:洗车 int mn mmin P3592 MYJ MAXN POI2015 dp

题目描述

有 \(n\) 家洗车店从左往右排成一排,每家店都有一个正整数价格 \(p_i\)。有 \(m\) 个人要来消费,第 \(i\) 个人会驶过第 \(a_i\) 个开始一直到第 \(b_i\) 个洗车店,且会选择这些店中最便宜的一个进行一次消费。但是如果这个最便宜的价格大于 \(c_i\),那么这个人就不洗车了。请给每家店指定一个价格,使得所有人花的钱的总和最大。

分析

固然,先对 \(c\) 离散化一下。另一方面,一眼发现该题是一个区间dp。
由题意可知,某个区间内的最小值是影响答案的关键元素,不妨先定义一下状态 \(dp[l][r][mn]\),表示当区间 \([l,r]\) 内的最低价格为 \(mn\) 时在这个区间所能获得的最大收益。

那么,到现在为止,能完成转移吗?不太行。
原因是你无从得知究竟有多少车主在区间 \([l,r]\)会考虑洗车。而车主是否会洗车,又取决于该区间内的点的最小值与车主的期望值的关系。因此,不妨预处理出一个 \(cost[i][j]\),表示如果第 \(i\) 家洗车店的价格为 \(j\),会有多少个车主可能到这里洗车。

接下来,就需要在区间 \([l,r]\) 中枚举一个 \(k\),令这家洗车店的价格为 \(mn\),由此,就能将当前区间分成左右两个部分,这就体现出了笛卡尔树的感觉了(把你枚举的这个 \(k\) 当作树根,左右两个区间分别为左右子树,显然,左右两个区间的最小值都是大于或等于树根 \(k\) 对应的值 \(mn\) 的)。

总的dp方程就是:

\[dp[l][r][mn] = max(dp[l][r][mn],dp[l][k][x >= k] + dp[k + 1][r][y >= k] + k * cost[k][mn]) \]

由于最后要求输出方案,转移的时候还需要记录一下某个区间对应的取 \(mn\) 时的 \(k\),这点细节不太好处理。我的代码是直接用定义的 dp 数组来记录后缀max,因此在处理记录的决策信息时似乎有点抽象?烦读者自行理解。

另一方面,注意到,dp[l][k][x >= k] 和dp[k + 1][r][y >= k]这两个东西可以通过后缀取max优化掉,所以总的时间复杂度为 \(O(n^3m)\)。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 55;
vector<int> lsh;
int n,m,cost[MAXN + 5][4015],
dp[MAXN + 5][MAXN + 5][4015],f[MAXN + 5][MAXN + 5][4015],last[MAXN + 5][MAXN + 5][4015],out[MAXN + 5];
struct NPE{
    int a,b,c;
}p[4005];
void print(int l,int r,int mmin){
    if(l == 0 || r == 0)return;
    if(l > r)return;
    int k = f[l][r][mmin];
    out[k] = last[l][r][mmin];
    print(l,k - 1,last[l][r][mmin]);
    print(k + 1,r,last[l][r][mmin]);
}
bool cmp(NPE a,NPE b){
    return a.c < b.c;
}
signed main(){
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++){
        for(int j = i; j <= n; j++){
            for(int k = 1; k <= m; k++)dp[i][j][k] = -1;
        }
    }
    for(int i = 1; i <= m; i++){
        scanf("%d%d%d",&p[i].a,&p[i].b,&p[i].c);
    }
    sort(p + 1,p + 1 + m,cmp);
    for(int len = 1; len <= n; len++){
        for(int l = 1; l + len - 1 <= n; l++){
            int r = len + l - 1;
            for(int i = l; i <= r; i++){
				for(int j = 1; j <= m; j++){
					cost[i][j] = 0;
                }
            }
            for(int j = 1; j <= m; j++){
				if(p[j].a >= l && p[j].b <= r){
					for(int i = p[j].a; i <= p[j].b; i++){
						cost[i][j]++;
                    }
                }
            }
			for(int i = l; i <= r; i++){
				for(int j = m; j; j--){
					cost[i][j] += cost[i][j+1];
                }
            }
            for(int k = l; k <= r; k++){
                for(int mmin = 1; mmin <= m; mmin++){
                    if(dp[l][r][mmin] < dp[l][k-1][mmin] + dp[k + 1][r][mmin] + p[mmin].c * cost[k][mmin]){
						dp[l][r][mmin] = dp[l][k-1][mmin] + dp[k + 1][r][mmin] + p[mmin].c * cost[k][mmin];
                        f[l][r][mmin] = k;
                        last[l][r][mmin] = mmin;
                    }
                }
            }
            for(int mmin = m; mmin >= 1; mmin--){
                if(dp[l][r][mmin] < dp[l][r][mmin + 1]){
                    dp[l][r][mmin] = max(dp[l][r][mmin],dp[l][r][mmin + 1]);
                    f[l][r][mmin] = f[l][r][mmin + 1];
                    last[l][r][mmin] = last[l][r][mmin + 1];
                }
                
            }
        }
    }
    print(1,n,1);
    cout << dp[1][n][1] << "\n";
    for(int i = 1; i <= n; i++){
        cout << p[out[i]].c << " ";
    }
}

标签:洗车,int,mn,mmin,P3592,MYJ,MAXN,POI2015,dp
From: https://www.cnblogs.com/CZ-9/p/17366907.html

相关文章

  • POI2015
    KUR首先设\(z=ai+b\),考虑求出\(z\)的范围。假设序列的第\(j\)位为\(1\),则\(z+a(j-1)\geqp\),即将\([p,n)\)区间向左循环位移\(a(j-1)\),然后对所有这样的区间取交。由于循......
  • 题解 洛谷 P3594 [POI2015] WIL
    1.题面描述题目链接给定一个长度为\(n\)的序列,你有一次机会选中一段连续的长度不超过\(d\)的区间,将里面所有数字全部修改为\(0\)。请找到最长的一段连续区间,使得该......
  • TypeScript中使用NodeJs日期格式化库myjs-common
    依赖包安装#安装myjs-common包npminstallmyjs-common@1.0.0格式器表达式YEAR_FORMAT:年格式化-yyyyMONTH_FORMAT:月格式化-yyyy-MMDATE_FORMAT:日期格式化-yyyy-MM-ddH......
  • BZOJ3747-[POI2015]Kinoman
    Description共有m部电影,编号为1~m,第i部电影的好看值为w[i]。在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部。你可以选择l,r(1<=l<=r<=n),并观......
  • 洛谷 P3592
    首先不难发现最终答案中只会出现\(c_i\)中的数,所以可以将\(c_i\)离散化。设\(f_{i,j,k}\)表示区间\([l,r]\),最小值不小于\(k\)的最大收益,\(cnt_{i,j}\)表示区间......
  • BZOJ 3747: [POI2015]Kinoman
    题目链接:​​传送门​​好像之前在洛谷上做过一个叫KIN的题一个电影看多次就不会记贡献那么这个电影产生贡献的区间就是(这一次看)到(上一次看的后一天)在这一块内才会记录它......
  • 【题解】P3583 [POI2015] KWA
    模拟赛出这道题???还好赛时乱搞做出来了(/hanxlinkDescription定义一个数\(n\)的拆分为:将\(n\)表示为若干个不同的正整数的平方和。令\(k(n)\)为\(n\)的拆分中最......
  • POI2015 合集
    KUR题面考虑小串会在大串的哪些位置出现,然后就是设小串开头的位置为\(x\),然后小串第\(i\)个位置如果\(a_i=0\),则\(0\leqa(x+i)+b<p(\modn)\),\(a_i=1\)同理,然......
  • P3592 [POI2015] MYJ 洗车店
    P3592[POI2015]MYJ洗车店点击查看代码//此题与人的区间[a,b]有关:区间DP;将[l,p-1],[p+1][r]的区间递归计算,经过p的区间//f[l][r][k]表示l<=a<=b<=r的洗车店的价......