小奇探险


在这里插入图片描述

题目

小奇去遗迹探险,遗迹里有N个宝箱,有的装满了珠宝,有的装着废品。
小奇有地图,所以它知道每一个宝箱的价值,但是它不喜欢走回头路,所以要按顺序拿这N个宝箱中的若干个。

拿宝箱很累的。一开始小奇的体力是1 ,每得到一个宝箱之后,小奇得到的价值是体力x 宝箱的价值,之后它的体力就会变为原来的K倍 。

小奇不喜欢连续放过很多宝箱,所以任意一段长度为M的序列中,小奇一定要取走其中的一个宝箱。

现在小奇想知道它能得到的最大价值和。

输入格式
第一行,两个整数 N,M,表示的含义如题目中所述;
第二行,一个小数K ,表示的含义如题目中所述,最多4位小数;
第三行,N个整数,第i个整数表示第i个宝箱的价值。

输出格式
输出一行,一个实数,表示小奇能得到的最大价值和,四舍五入保留两位小数。

在这里插入图片描述

题解

特别水也特别板的一道题。。。。
每个人都会想到三维DP
D P [ i ] [ j ] [ k ] DP[i][j][k] DP[i][j][k]:表示第i个宝箱一定选,上一次选的宝箱为j,选了k个
在这里插入图片描述
通过数据范围,就已经扼杀了想开三维数组的心


那我就先舍弃k,假设没有这个k的限制来思考一下
因为开二维都会MLE,所以就死命也要把DP压成一维
D P [ i ] DP[i] DP[i]:表示第i个宝箱一定选,前面 [ 1 , i − 1 ] [1,i-1] [1,i1]不管怎么选,反正要是合法的最大价值
转移方程式?
D P [ i ] = D P [ j ] + w [ i ] ( j ∈ [ i − m , i − 1 ] ) DP[i]=DP[j]+w[i](j∈[i-m,i-1]) DP[i]=DP[j]+w[i](j[im,i1])
这就是一个滑动窗口的优化(单调队列)我要AC了!!!
在这里插入图片描述


但是因为有了k的限制,我们就要重新搞一下了
我们思考如果正着往后推,那么前面选了j个就会影响后面的价值
但是如果从后往前推,后面管他选了x个,对当前i的价值是没有影响的,
当前i的价值只被前面的选取决定
在这里插入图片描述
所以重新定义一下 D P [ i ] DP[i] DP[i]
表示从n往1推,第i个宝箱必须选,且 [ i + 1 , j ] [i+1,j] [i+1,j]的选取必须是合法的最大价值
转移方程式也就变了?
D P [ i ] = D P [ j ] + w [ i ] ( j ∈ [ i + 1 , m i n ( n + 1 , i + m ) ] ) DP[i]=DP[j]+w[i](j∈[i+1,min(n+1,i+m)]) DP[i]=DP[j]+w[i](j[i+1,min(n+1,i+m)])
在这里插入图片描述


这里我只解释两个地方:
1:为什么要压一个n+1??
因为数据有可能在最后的 [ n , n − m + 1 ) [n,n-m+1) [n,nm+1)全是负数,
而这个时候i距离n的距离又不到m
我就可以不选,这个时候就可以用 D P [ n + 1 ] = 0 DP[n+1]=0 DP[n+1]=0来处理

2:为什么可以取到i+m??
按我们的常规理解应该是 [ i + m − 1 , i ] [i+m-1,i] [i+m1,i]
仔细想想,i是一定要取的,那么 [ i + m − 1 , i ] [i+m-1,i] [i+m1,i]这个区间肯定是满足m的
但是 [ i + m , i − 1 ] [i+m,i-1] [i+m,i1]也是可以满足m的,所以i+m也可能对i进行贡献

举例说明:
m = 2,i = 1,i + m = 3
1 2 3

i
[1,2]长度是m,其中有下标为1的宝藏被拿了
[2,3]长度也是m,所以下标为3的宝藏也应该被拿

代码实现

#include <cstdio>
#include <deque>
using namespace std;
#define MAXN 100005
deque < int > deq;
int n, m;
double k, result = -0x7f7f7f7f;
double w[MAXN], dp[MAXN];

int main() {
	scanf ( "%d %d %lf", &n, &m, &k );
	for ( int i = 1;i <= n;i ++ )
		scanf ( "%lf", &w[i] );
	deq.push_back ( n + 1 );
	for ( int i = n;i >= 1;i -- ) {
		while ( ! deq.empty() && deq.front() > i + m )
			deq.pop_front();
		dp[i] = dp[deq.front()] * k + w[i];
		while ( ! deq.empty() && dp[deq.back()] <= dp[i] )
			deq.pop_back();
		deq.push_back ( i );
	}
	for ( int i = 1;i <= m;i ++ )
		result = max ( result, dp[i] );
	printf ( "%.2lf", result );
	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值