首先特殊判断一下所给出的数据是否能够得到我们想要的最终结果,如果可以,那么就开始进行动态规划的过程:记录所拥有的物品质量之和,然后二分法开始判断,对于选定的质量假设为amount是否能够保证在满足题意的条件下得到相应的解。dp[i][j]表示的是目前利用i个灯泡能够得到的在所有半段的最重的重量不超过amount的时候所得到的段的个数。其中为了便于后续状态更加方便的转移,使用j来标志当前所拥有的段的个数是奇数个还是偶数个,0对应偶数,1对应奇数,然后让i从2开始增加,每次增加的步长为2(依据题意得到的)。对于每一个i找出我们所能进行添加灯的方案的最小值,并且记录,作为后面的参照,最后比较即可,具体实现见如下代码:
#include<iostream>
#include<vector>
#include<string>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<sstream>
#include<cstdio>
#include<deque>
using namespace std;
class Solve{
public:
int n, m, d;
int data[40005], sum[40005];
void Init(){
sum[0] = 0;
for (int i = 1; i <= n; i++){
cin >> data[i];
sum[i] = sum[i - 1] + data[i];
}
}
bool DP(int amount){
int dp[40005][2];
int INF = 1 << 30;
dp[0][0] = 0;
dp[0][1] = INF;
for (int i = 2; i <= n; i += 2){
dp[i][0] = INF;
dp[i][1] = INF;
for (int len = 1; len <= d&&i - len * 2 >= 0; len++){
if (sum[i] - sum[i - len] > amount) break;
if (sum[i - len] - sum[i - 2 * len] <= amount){
dp[i][0] = min(dp[i][0], dp[i - 2 * len][1] + 1);
dp[i][1] = min(dp[i][1], dp[i - 2 * len][0] + 1);
}
}
}
if (dp[n][(m - 1) % 2] > m - 1) return false;
return true;
}
void Deal(){
Init();
int left = 1, right = sum[n];
if ((m-1)*2*d<n||n%2||n<2*(m-1)){
cout << "BAD" << endl;
return;
}
while (left < right){
int mid = (left + right) / 2;
if (!DP(mid)) left = mid + 1;
else right = mid;
}
cout << left << endl;
}
};
int main(){
int Z;
cin >> Z;
Solve a;
while (Z--){
cin >> a.n >> a.m >> a.d;
a.Deal();
}
}