题意:n堆垃圾,任意连续的m堆中最多选q堆,求最大垃圾量。
思路:dp[i][S]表示前 i 个人中,第i+1-m个人到第i个人的选取情况为S时,能获得的最大值。
先说第一个比较简单的转移方程:dp[i][S],若S里1的个数大于q,则为0。否则在第i-m堆垃圾的取和不取中选较大值,再加上第i堆的取舍情况。
int cnt = cal(j);//计算出j里面1的个数 if (cnt <= q){ dp[i][j] = max(dp[i - 1][j >> 1], dp[i - 1][(j >> 1) | (1 << (m - 1))]) + (j & 1)*w[i]; }
代码如下:
#pragma warning(disable:4996) #include <cstdio> #include <algorithm> #include <cstring> using namespace std; int w[1005], dp[1005][1 << 10]; int cal(int x){ int ret = 0; while (x){ if (x & 1)ret++; x >>= 1; } return ret; } int main(){ freopen("in.txt", "r", stdin); int n, m, q; scanf("%d %d %d", &n, &m, &q); for (int i = 1; i <= n; i++)scanf("%d", w + i); memset(dp, 0, sizeof dp); dp[1][1] = w[1]; for (int i = 2; i <= n; i++){ for (int j = 0; j < (1 << m); j++){ int cnt = cal(j);//计算出j里面1的个数 if (cnt <= q){ dp[i][j] = max(dp[i - 1][j >> 1], dp[i - 1][(j >> 1) | (1 << (m - 1))]) + (j & 1)*w[i]; } } } int ans = 0; for (int j = 1; j < (1 << m); j++){ ans = max(ans, dp[n][j]); } printf("%d\n", ans); return 0; }
//这里就是容易出错的地方 /*int s1 = (j << 1) | 1; int s0 = (j << 1);*/ int s0 = ((j << 1) & ((1 << m) - 1)); int s1 = ((j << 1 | 1) & ((1 << m) - 1)); if (s0 >= (1 << m))continue; if (cal(s1) <= q){ dp[i][s1] = max(dp[i][s1], dp[i - 1][j] + w[i]); } dp[i][s0] = max(dp[i][s0], dp[i - 1][j]);
因为我们是用集合S来表示m个元素的取舍情况,但是j<<1的话可能是S的元素个数大于m(这时候就容易漏掉状态),所以我们要按位与上m个1,即(1<<m)-1。