有n个cd,每个cd的时长为w[i],现在有一个能记录时长为s的磁带,要求出选哪些cd能使磁带剩余的时间段尽量少,不能只记录cd的一部分。
使用01背包的思路可以解决,因为要记录答案了,所以不能使用滚动数组了,dp[i][j]表示前i个cd在磁带为j的时候记录的最大时间,因为要记录路径,我们可以使用一个
pre[i][j]表示在状态i,j第i个物品有没有使用。然后,根据pre数组就可以打印出来答案了。另外还可以根据dp数组打印,但要注意判断的方式,可能方式部队就错了,我RE了好几次。
还因为没注意输出格式PE了一次。
代码如下:
/*************************************************************************
> File Name: 624.cpp
> Author: gwq
> Mail: gwq5210@qq.com
> Created Time: 2014年11月16日 星期日 01时21分13秒
************************************************************************/
#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>
#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())
using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef long long ll;
const double esp = 1e-5;
#define N 22
//pre数组存放状态为i,j时候使用了没有第i个cd。
//也可以使用dp数组的特性直接打印解,但是要注意边界。
int w[N], pre[N][N * 10000], dp[N][N * 10000], n, s, ans;
/*
* 因为要打印解,所以就不能使用滚动数组了。
*/
void solve(void)
{
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= s; ++j) {
dp[i][j] = (i == 1) ? 0 : dp[i - 1][j];
pre[i][j] = 0;
if (j >= w[i]) {
if (dp[i][j] < dp[i - 1][j - w[i]] + w[i]) {
dp[i][j] = dp[i - 1][j - w[i]] + w[i];
pre[i][j] = 1;
}
}
}
}
int i = n;
int j = s;
stack<int> res;
while (i >= 1) {
//if (pre[i][j] == 1) {
//应该是存在这样一种情况dp[i - 1][j]和dp[i - 1][j - w[i]] + w[i]
//相等,这是根据上边if语句,应该没有选择第i个数,但是这里认为选
//了,所以就产生了RE。
//if (dp[i][j] == dp[i - 1][j - w[i]] + w[i]) {
if (dp[i][j] != dp[i - 1][j]) { //写成这样就过了
res.push(w[i]);
j -= w[i];
}
--i;
}
while (!res.empty()) {
printf("%d ", res.top());
res.pop();
}
ans = dp[n][s];
}
int main(int argc, char *argv[])
{
while (scanf("%d%d", &s, &n) != EOF) {
for (int i = 1; i <= n; ++i) {
scanf("%d", &w[i]);
}
ans = 0;
solve();
printf("sum:%d\n", ans);
}
return 0;
}
/*
CD
You have a long drive by car ahead. You have a tape recorder, but
unfortunately your best music is on CDs. You need to have it on tapes so
the problem to solve is: you have a tape N minutes long. How to choose
tracks from CD to get most out of tape space and have as short unused
space as possible.
Assumptions:
number of tracks on the CD. does not exceed 20
no track is longer than N minutes
tracks do not repeat
length of each track is expressed as an integer number
N is also integer
Program should find the set of tracks which fills the tape best and
print it in the same sequence as the tracks are stored on the CD
Input
Any number of lines. Each one contains value N, (after space) number of
tracks and durations of the tracks. For example from first line in sample
data: N=5, number of tracks=3, first track lasts for 1 minute, second one
3 minutes, next one 4 minutes
Output
Set of tracks (and durations) which are the correct solutions and string
``sum:" and sum of duration times.
Sample Input
5 3 1 3 4
10 4 9 8 4 2
20 4 10 5 7 4
90 8 10 23 1 2 3 4 5 7
45 8 4 10 44 43 12 9 8 2
Sample Output
1 4 sum:5
8 2 sum:10
10 5 4 sum:19
10 23 1 2 3 4 5 7 sum:55
4 10 12 9 8 2 sum:45
*/