#include <stdio.h> /** * 原题: * 有N种不同大小的数字a[i], 每种各m[i]个,判断是否可以从这些数字之中 * 选出若干个使它们的和恰好为K */ #define N 6 #define K 35 static int a[N] = {3,5,8,6,10,11}; static int m[N] = {3,2,2,2, 3, 3}; /** * 思路如下: * 设置子问题:选择前i(i<=N)种数字,判断是否可以从这些数字中选出若干个,使它们的和恰好为j(j<=K), * res=1代表可以, res=0代表不可以 * 确定边界:i=N时, res=0 , j=0时, res=1 */ //解法1:递归 int solve_1(int i, int j){ if (j == 0) return 1; if (i == N) return 0; for (int k = 0; k <= m[i] && j >= a[i]*k; ++k) { if (solve_1(i+1, j-a[i]*k)) return 1; } return 0; } //解法2:递归+记忆数组 static int memo[N+1][K+1]; int solve_2(int i, int j){ if (memo[i][j] > -1) return memo[i][j]; if (j == 0) return memo[i][j] = 1; if (i == N) return memo[i][j] = 0; for (int k = 0; k <= m[i] && j >= a[i]*k; ++k) { if (solve_2(i+1, j-a[i]*k)) return memo[i][j] = 1; } return memo[i][j] = 0; } //解法3:递推 static int dp[N+1][K+1]; int solve_3(){ for (int j = 0; j <= K; ++j) { dp[N][j] = 0; } for (int i = 0; i <= N; ++i) { dp[i][0] = 1; } for (int i = N-1; i >= 0; --i) { for (int j = 1; j <= K; ++j) { for (int k = 0; k <= m[i] && j >= a[i]*k; ++k) { if (dp[i+1][j-a[i]*k]) dp[i][j] = 1; } } } return dp[0][K]; } //解法4:递推优化 static int dpx[K+1]; int solve_4(){ for (int i = 0; i <= N; ++i) { dpx[i] = -1; } dpx[0] = 0; for (int i = 0; i < N; ++i) { for (int j = 0; j <= K; ++j) { if (dpx[j] >= 0){ dpx[j] = m[i]; } else if (j < a[i] || dpx[j-a[i]] <= 0){ dpx[j] = -1; } else { dpx[j] = dpx[j-a[i]] - 1; } } } return dpx[K] >= 0; } int main() { printf("solve_1:%d\n", solve_1(0, K)); for (int i = 0; i <= N ; ++i) { for (int j = 0; j <= K; ++j) { memo[i][j] = -1; } } printf("solve_2:%d\n", solve_2(0, K)); printf("solve_3:%d\n", solve_3()); // for (int i = 0; i <= N ; ++i) { // for (int j = 0; j <= K; ++j) { // printf("%d ", dp[i][j]); // } // printf("\n"); // } printf("solve_4:%d\n", solve_4()); return 0; }
运行结果:
solve_1:1
solve_2:1
solve_3:1
solve_4:1