zoukankan      html  css  js  c++  java
  • @bzoj


    @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

    @solution@

    首先,洗车店的价格一定是某顾客的 c 值,否则可以通过调整变得更优。

    像这样的区间最值相关问题可以考虑其笛卡尔树的结构。比如这道题就可以用笛卡尔树来 dp。

    具体来讲,我们定义状态 dp(i, j, k),表示区间 [i, j] 的最小值为 k 能够取得的最大利益。根据最先的结论,我们 k 的取值只有 O(m) 个。

    通过枚举 [i, j] 的最小值位置 p,我们可以进行转移:

    [dp(i, j, k) = dp(i, p-1, x) + dp(p+1, j, y) + f(i, j, p, k) ]

    其中要求 x, y >= k,权值函数 f(i, j, p, k) 表示这一个状态能够赚得的顾客利益。

    f(i, j, p, k) 会计算到哪些顾客呢?假如第 q 个顾客被计算到了,那么必然有 i <= a[q] <= p <= b[q] <= j 且 c[q] >= k。
    通过从大到小枚举 k,可以快速地维护 f。

    我们通过维护后缀最大值,就可以不必再枚举 x 和 y。

    因为要输出方案,记得记录当前状态是怎么转移过来的。最后递归输出即可。

    总时间复杂度 O(n^3*m)。

    @accepted code@

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXN = 50;
    const int MAXM = 4000;
    struct node{
    	int le, ri;
    	int lim;
    }a[MAXM + 5];
    bool operator < (node a, node b) {
    	return a.lim < b.lim;
    }
    int n, m;
    int ans[MAXN + 5];
    int mx[MAXN + 5][MAXN + 5][MAXM + 5];
    int dp[MAXN + 5][MAXN + 5][MAXM + 5];
    int val[MAXN + 5][MAXN + 5][MAXN + 5];
    int pre[MAXN + 5][MAXN + 5][MAXM + 5];
    int mxpos[MAXN + 5][MAXN + 5][MAXM + 5];
    void GetAnswer(int le, int ri, int p) {
    	if( mx[le][ri][p] == 0 ) {
    		for(int i=le;i<=ri;i++)
    			ans[i] = a[p].lim;
    		return ;
    	}
    	int q = mxpos[le][ri][p];
    	int r = pre[le][ri][q];
    	ans[r] = a[q].lim;
    	GetAnswer(le, r-1, q);
    	GetAnswer(r+1, ri, q);
    }
    int main() {
    	scanf("%d%d", &n, &m);
    	for(int i=1;i<=m;i++)
    		scanf("%d%d%d", &a[i].le, &a[i].ri, &a[i].lim);
    	sort(a+1, a+m+1);
    	for(int len=1;len<=n;len++)
    		for(int i=1;i+len-1<=n;i++) {
    			int j = i + len - 1;
    			for(int k=m;k>=1;k--) {
    				if( i <= a[k].le && a[k].ri <= j ) {
    					for(int l=i;l<=j;l++) {
    						if( a[k].le <= l && l <= a[k].ri )
    							val[i][j][l]++;
    						if( mx[i][l-1][k] + mx[l+1][j][k] + val[i][j][l]*a[k].lim > dp[i][j][k] ) {
    							pre[i][j][k] = l;
    							dp[i][j][k] = mx[i][l-1][k] + mx[l+1][j][k] + val[i][j][l]*a[k].lim;
    						}
    					}
    				}
    				if( k == m || dp[i][j][k] >= mx[i][j][k+1] ) {
    					mx[i][j][k] = dp[i][j][k];
    					mxpos[i][j][k] = k;
    				}
    				else {
    					mx[i][j][k] = mx[i][j][k+1];
    					mxpos[i][j][k] = mxpos[i][j][k+1];
    				}
    			}
    	}
    	printf("%d
    ", mx[1][n][1]);
    	GetAnswer(1, n, 1);
    	for(int i=1;i<=n;i++)
    		printf("%d ", ans[i]);
    	puts("");
    }
    

    @details@

    其实不需要离散化,因为即使 c 值重复也影响不大。

    一开始因为区间 dp 的某些错误 dp 方式 WA 了几次(捂脸)。

  • 相关阅读:
    redux的理解
    跨域的三种解决方案
    HTTP缓存字段总结
    src属性与浏览器渲染
    ES6之promise原理
    http header Content-Type之常用三种
    用css实现正方形div
    js判断数据类型
    react中PureComponent浅对比策略
    React中自己实现选项卡动画
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10359188.html
Copyright © 2011-2022 走看看