因为同时有两个要求(数量最多、数量相同时长度最长),所以要维护两个数组。
因为T一定小于所有歌的长度的和,所以T上限是9678。不过如果T真的有10^9那么大应该如何处理?动态规划还适用吗?
我尝试用记忆化搜索WA了,很奇怪。换成递推就直接过了。
递推时,因为歌曲的出现顺序并不重要,所以在读取数据的时候,下标可以逆序从N-1到0,这样可以实现边读入边处理。如果歌曲顺序有要求的话,也可以换一下状态的定义,改为“dp(i,j)表示在时间 j 内唱前 i 首歌,最多可以唱多少首”(原本是 i 到N-1),和书中的0-1背包一样。
因为不需要打印解,滚动数组也可以。加上边读入边处理的优化,速度会稍微快一些。要注意内层循环的顺序。
滚动数组:Run Time: 0.039s
#define UVa "LT9-5.12563.cpp" //Jin Ge Jin Qu hao char fileIn[30] = UVa, fileOut[30] = UVa; #include<cstring> #include<cstdio> #include<algorithm> using namespace std; //Global Variables. Reset upon Each Case! const int maxn = 50 + 10, maxt = 10000; //180 * n + 678 <= 180*50+678 = 9678 < 10000 int songs[maxn]; int dp[maxt]; int len[maxt]; int N, T; ///// int main() { int K; scanf("%d", &K); for(int kase = 1; kase <= K; kase ++) { scanf("%d%d", &N, &T); for(int i = 0; i < maxt; i ++) { dp[i] = 1; len[i] = 678; } int song, ans = -1, anslen = -1; for(int i = 0; i < N; i ++) { scanf("%d", &song); for(int j = T; j >= 0; j --) { if(j > song) { int tmp = dp[j - song] + 1; if(tmp > dp[j]) { dp[j] = tmp; len[j] = len[j - song] + song; } else if(tmp == dp[j]) len[j] = max(len[j], len[j - song] + song); } if(ans < dp[j]) { ans = dp[j]; anslen = len[j]; } else if(ans == dp[j]) anslen = max(anslen, len[j]); } } printf("Case %d: %d %d\n", kase, ans, anslen); } return 0; }
普通递推:Run Time: 0.053
#define UVa "LT9-5.12563.cpp" //Jin Ge Jin Qu hao char fileIn[30] = UVa, fileOut[30] = UVa; #include<cstring> #include<cstdio> #include<algorithm> using namespace std; //Global Variables. Reset upon Each Case! const int maxn = 50 + 10, maxt = 10000; //180 * n + 678 <= 180*50+678 = 9678 < 10000 int songs[maxn]; int dp[maxn][maxt]; int len[maxn][maxt]; int N, T; ///// int main() { int K; scanf("%d", &K); for(int kase = 1; kase <= K; kase ++) { scanf("%d%d", &N, &T); for(int i = 0; i < N; i ++) scanf("%d", &songs[i]); for(int i = 0; i < maxt; i ++) { dp[N][i] = 1; len[N][i] = 678; } for(int i = N - 1; i >= 0; i --) { for(int j = 0; j <= T; j ++) { dp[i][j] = dp[i+1][j]; len[i][j] = len[i+1][j]; if(j > songs[i]) { int tmp = dp[i+1][j - songs[i]] + 1; if(tmp > dp[i][j]) { dp[i][j] = tmp; len[i][j] = len[i+1][j - songs[i]] + songs[i]; } else if(tmp == dp[i][j]) len[i][j] = max(len[i][j], len[i+1][j - songs[i]] + songs[i]); } } } printf("Case %d: %d %d\n", kase, dp[0][T], len[0][T]); } return 0; }