问题描述
解决方法
由于每个位置是否清扫,取决于该位置之前的M-1个位置是否清扫过,因此状态可以表示为:
best(i,p0,p1,p2,...pm−1)
p0
表示第i个位置是否清扫,1, 清扫,0, 不清扫。
p1
表示第i-1位置是否清扫,1, 清扫,0, 不清扫。
…
pm−1
表示第i-(m-1)个位置是否清扫,1, 清扫,0, 不清扫。
best(i,p0,p1,p2,...pm−1)
表示确定了从i-(m-1) 到i的清扫状态后,可以取得的最大值。
由于状态变量太多,不容易编程实现,状态压缩的含义就是用二进制串表示
p0,p1,p2,...pm−1
这些状态变量。
由于二进制串表示后,有些状态是不满足条件的,如果for循环填表,要考虑很多细节的东西,我们可以使用记忆搜索的方法,虽然效率上慢点,但是程序简洁了不少,也容易理解。
最后上代码:
#include <cstdio>
#include<cstring>
#include <algorithm>
using namespace std;
enum {maxn = 1000+4, maxs = (1<<10)+3};
int dp[maxn][maxs];
int w[maxn];
int n, m, q;
int bitCount(int i)
{
int num =0;
while(i){
if (i & 0x01)
num++;
i>>=1;
}
return num;
}
int best(int i, int s)
{
if (dp[i][s] >= 0)
return dp[i][s];
int num = bitCount(s);
if (num >q)
return dp[i][s] = 0;
dp[i][s] = max(best(i-1, s>>1), best(i-1, (s>>1) + (1<<(m-1))));
if (s & 0x01)
{
return dp[i][s] += w[i];
}else{
return dp[i][s];
}
}
#define OJ
int main()
{
#ifndef OJ
freopen("in.txt", "r", stdin);
#endif // OJ
scanf("%d %d %d", &n, &m, &q);
memset(dp, -1, maxn * maxs*sizeof(int));
memset(dp, 0, maxs*sizeof(int));
for (int i=1; i<= n; i++)
scanf("%d", w+i);
int res=-1;
for (int i=0; i<= (1<<m)-1; i++)
{
res = max(res, best(n, i));
}
printf("%d\n", res);
return 0;
}
解法二: 从前往后递推:
#include <cstdio>
#include <cstring>
#include <algorithm>
int n, m, q;
enum {maxn =1000+4, maxm2 = 1<<11};
int dp[maxn][maxm2];
int numOne[maxm2];
int getNumOne(int a)
{
int cnt =0;
for (; a; a>>=1)
if (a&0x01)
cnt++;
return cnt;
}
int main()
{
scanf("%d %d %d", &n, &m, &q);
memset(dp, 0, sizeof(dp));
for (int i=0; i< 1<<m; i++)
numOne[i] = getNumOne(i);
for (int i=0; i< n;i++)
{
int v;
scanf("%d", &v);
for (int j=0; j< (1<<m); j++)
{
if (numOne[j] <= q)
{
int k = j>>1;
dp[i+1][k] = std::max(dp[i+1][k], dp[i][j]); // no chose;
k = j>>1 | (1<<(m-1));
if (numOne[k] <=q)
dp[i+1][k] = std::max(dp[i+1][k], dp[i][j] + v);
}
}
}
int res =0;
for (int i=0; i< (1<<m); i++)
res = std::max(res, dp[n][i]);
printf("%d\n", res);
}