题目大意
给定N个位置,每个位置i都有一个value[i]值,从中选择若干个位置,使得连续的M个位置中的被选中的位置数目不超过Q,求出所有选择方案中value和最大的方案,输出其最大value和。
分析
1、可以采用枚举的方式,枚举出所有的选择可能情况,每次枚举成功后,更新全局变量最大值即可。时间复杂度O(2^N).当然会超时...
//index为dfs搜索到的位置,num_in_last_M 为前M个位置中有多少个已经被选中, //total_value为搜索到当前位置时,总的value void dfs(int index, int num_in_last_M, int total_value){ if (index == N){ //递归出口 max_value = max_value > total_value ? max_value : total_value; return; } //当前index处的位置不选中 dfs(index + 1, num_in_last_M, total_value); if (index < M){ if (num_in_last_M < Q){ used[index] = true; //used[i] 用于表示位置i是否被选中 dfs(index + 1, num_in_last_M + 1, total_value + wastes[index]); } } else if(num_in_last_M - used[index - M] < Q){ used[index] = true; dfs(index + 1, num_in_last_M - used[index - M] + 1, total_value + wastes[index]); } //回溯!!! used[index] = false; }
2、采用动态规划的方式
dp[i][j] 表示前i个位置中,位置(i-M+1, i-M+2, ... i)构成的选中情况的二进制表示(0表示未选中,1表示选中)为j时,可以得到的最大值。
问题满足最优化子结构:从dp[i-1][k] 过渡到dp[i][j]时,如果dp[i][j]取得了最大值,那么dp[i-1][k]也一定为状态(i-1, k)的最大值; 问题无后效性:从dp[i-1][k]推演到dp[i][j]时,不关心 状态(i-1,k)是如何得到的,只关心最后的结果。
在求完dp数组之后,如果想要获得最后的结果,需要遍历dp[N]K来获得所有情况的最大值。
int minValue(int a, int b){ return a < b ? a : b; } int main(){ scanf("%d %d %d", &N, &M, &Q); for (int i = 0; i < N; i++){ scanf("%d", &wastes[i]); } memset(used, false, sizeof(used)); memset(dp, 0, sizeof(dp)); for (int i = 1; i <= N; i++){ //枚举状态(i-1,j)的所有情况j(共1 << minValue(M, i-1)个) for (int j = 0; j < (1 << minValue(M, i-1)); j++){ int total = 0; //计算前M-1个位置中有多少个1 int k = j &((1 << minValue(M-1, i - 1)) -1); while (k){ total += (k & 1); k >>= 1; } //从状态(i-1, j)推演到(i, k) k = ((j << 1) & (1 << minValue(M, i)) - 1); //位置i处不选中 dp[i][k] = dp[i][k] > dp[i - 1][j]? dp[i][k]: dp[i-1][j]; if (total < Q){ //位置i处选中 k |= 1; dp[i][k] = dp[i][k] > dp[i - 1][j] + wastes[i - 1]? dp[i][k]: dp[i-1][j] + wastes[i-1]; } } } max_value = 0; //遍历求最大值 for (int i = 0; i < (1 << minValue(M, N)); i++){ max_value = max_value > dp[N][i] ? max_value : dp[N][i]; } printf("%d\n", max_value); return 0; }
时间: 2024-10-09 23:08:37