题目链接
看博客一律都说很简单,还有各种骚操作。体验好差。
考虑第到第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
#include
#include
#include
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<1)]) + num[i];
else dp[i][j] = max(dp[i - 1][j / 2],dp[i - 1][j/2 + (1<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);
}
}