题目
题目描述
小奇去遗迹探险,遗迹里有 个宝箱,有的装满了珠宝,有的装着废品。
小奇有地图,所以它知道每一个宝箱的价值,但是它不喜欢走回头路,所以要按顺序拿这 个宝箱中的若干个。
拿宝箱很累的。一开始小奇的体力是 ,每得到一个宝箱之后,小奇得到的价值是体力 宝箱的价值,之后它的体力就会变为原来的 倍 。
小奇不喜欢连续放过很多宝箱,所以任意一段长度为 的序列中,小奇一定要取走其中的一个宝箱。
现在小奇想知道它能得到的最大价值和。
输入格式
第一行,两个整数 ,表示的含义如题目中所述;
第二行,一个小数 ,表示的含义如题目中所述,最多 位小数;
第三行, 个整数,第 个整数表示第 个宝箱的价值。
输出格式
输出一行,一个实数,表示小奇能得到的最大价值和,四舍五入保留两位小数。
样例
样例输入
3 2
0.1
1 2 3
样例输出
2.30
题解&分析
首先不考虑k的情况,那么就是一个裸的dp+单调队列优化
直接一维枚举即可
可是这里会存在k,那么这个k有什么影响呢?
首先,如果我们还是用一维数组,它不满足dp的基本性质:无后效性
也就是说,前面选的个数是会影响后面的价值,可是正常dp后面的点是不能被影响的
所以就有两种解决方案:
第一种是直接将它扩大成二维
dp[i][j]表示到第i个点为止已经选了j个作为宝藏
那么dp[i][j] = dp[k][j-1] + k_{j-1}*a[i]
即使用了滚动数组与优化,也是n平方的时间复杂度
那么还有一种方法,就是从后往前去dp,dp[i]表示从i这个点开始且i必须选的最大往后找的最大价值,惊奇地发现dp式是长这样的:
dp[i] = dp[j] * k+ a[i]
那么就可以直接转移了
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
#define ll long long
const int MAXN = 1e5 + 3;
double dp[MAXN] , a[MAXN];
int n , m;
int head , tail , que[MAXN];
double k;
int main(){
scanf( "%d%d" , &n , &m );
scanf( "%lf" , &k );
for( int i = 1 ; i <= n ; i ++ ){
scanf( "%lf" , &a[i] );
}
dp[n+1] = 0;
head = tail = 1, que[head] = n + 1;
for( int i = n ; i >= 1 ; i -- ){
while( head < tail && que[head] - m > i ) head ++;
dp[i] = dp[que[head]] * k + a[i];
while( head <= tail && dp[que[tail]] <= dp[i] )
tail --;
que[++tail] = i;
}
for( int i = 1; i <= m; i ++ )
dp[1] = max( dp[1] , dp[i] );
printf( "%.2lf" , dp[1] );
return 0;
}
总结
这道题主要是要想到这里不满足无后效性,所以是二维dp
然后再去想使a[i]的值是一个可以取定的值
所以dp题应该要学会分析dp的性质才行