题目
小奇去遗迹探险,遗迹里有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,i−1]不管怎么选,反正要是合法的最大价值
转移方程式?
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∈[i−m,i−1])
这就是一个滑动窗口的优化(单调队列)我要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,n−m+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+m−1,i]
仔细想想,i是一定要取的,那么
[
i
+
m
−
1
,
i
]
[i+m-1,i]
[i+m−1,i]这个区间肯定是满足m的
但是
[
i
+
m
,
i
−
1
]
[i+m,i-1]
[i+m,i−1]也是可以满足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;
}