看博客一律都说很简单,还有各种骚操作。体验好差。
考虑第到第i个位置的情况,我们只需要知道前面m-1个位置的情况就可以了。所以将前面m-1加上i这m个位置压缩为一个状态,然后可以求出取了几个位置,如果取了超过q个就不再考虑。
考虑小于等于q的情况。基本的状态转移方程就是
dp[i][j]=max(dp[i−1][j>>1],dp[i−1][j/2+(1<<m−1)]);
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
>>
1
]
,
d
p
[
i
−
1
]
[
j
/
2
+
(
1
<<
m
−
1
)
]
)
;
这里
j/2+(1<<m−1)
j
/
2
+
(
1
<<
m
−
1
)
是表示这m个数之前的那一个数是1的情况,同理j/2就是代表之前的那一个数是0的情况。
然后就分为两种情况,就是第i位是否取的情况,直接将压缩后的状态j&1就可以判断当前j状态是否取了第i位(搞不懂为什么那么多人选择了不容易明白的方式。),如果取了第i位上面的方程就要加上第i位的垃圾数。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,q;
int num[1005],dp[1005][(1 << 10) + 5];
int main()
{
while(~scanf("%d %d %d",&n,&m,&q))
{
for (int i = 1; i <= n; i++)
{
scanf("%d",&num[i]);
}
memset(dp,0,sizeof dp);
int maxn = (1 << m);
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < 1 << (m); j++)
{
int temp = 0,x = j;
while(x)
{
temp += x&1;
x>>=1;
}
if(temp > q)continue;
if(j&1)dp[i][j] = max(dp[i - 1][j>>1],dp[i - 1][j/2 + (1<<m-1)]) + num[i];
else dp[i][j] = max(dp[i - 1][j / 2],dp[i - 1][j/2 + (1<<m-1)]);
}
}
int ans = 0;
for (int i = 0; i < (1<<(m)); i++)
{
if(dp[n][i] > ans)
ans = dp[n][i];
}
printf("%d\n",ans);
}
}