问题简介
详情请参考
hihocoder官网。从N个数字选取任意个数字使得数字和最大,需要满足连续M个数字中不选择超过Q个。
算法详解
这个问题可以利用动态规划求解,需要按数字枚举,且对于前M个是否选取的状态展开。时间复杂度为O(N*2^M),因为M不大于10,所以O(2^M)=O(1024)=O(1)。
具体的对于第i个数字,存储i-M+1到i这M个数字的2^M种选取情况下,从前i个数字中进行满足条件的选取可以得到的最大值。
问题在于状态是动态且不定的,所以需要对状态进行压缩。具体的,用一个数字即可表示所有的状态,从个位起第k个bit表示第i-k+1个数字是否选取,选取则为1,否则为0.
这样得到的转移方程即为
- f[i][j] = 0 if cnt[j] > Q
- f[i][j] = max(f[i-1][j>>1+1<<(M-1)],f[i-1][j>>1])+weight[j]; if j&1==1
- f[i][j] = max(f[i-1][j>>1+1<<(M-1)],f[i-1][j>>1]); if j&1==0
代码
#include <iostream>
using namespace std;
void build_cnt(int *cnt,int m){
int len = 1<<m;
for (int i = 0;i < len;++i){
cnt[i] = 0;
for (int j = 0;j < m;++j)
cnt[i] += ((i>>j)&1);
}
}
inline int mmax(int a,int b){
return (a>b)?a:b;
}
int main()
{
int n,m,q;
int weight[1002] = {0};
cin >> n >> m >> q;
for (int i = 0;i < n;++i)
cin >> weight[i];
int maxw[2][2050] = {0};
int cnt[2050];
bool flag = true;
int len = 1<<(m);
build_cnt(cnt,m);
// cout << "cnt: ";
// for (int i = 0;i < len;++i)
// cout << cnt[i] << ' ';
// cout << endl;
for (int i = 0;i < n;++i){
for (int j = 0;j < len;++j){
if (cnt[j] > q)
maxw[flag][j] = 0;
else {
if (j&1){
maxw[flag][j] = mmax(maxw[!flag][(j>>1)+(1<<(m-1))],maxw[!flag][(j>>1)])+weight[i];
}
else {
maxw[flag][j] = mmax(maxw[!flag][(j>>1)+(1<<(m-1))],maxw[!flag][(j>>1)]);
}
}
}
//
// cout << i << ": ";
// for (int j = 0;j < len;++j)
// cout << maxw[flag][j] << ' ';
// cout << endl;
flag = !flag;
}
int result = 0;
for (int i = 0;i < len;++i){
if (result < maxw[!flag][i])
result = maxw[!flag][i];
}
cout << result << endl;
return 0;
}