UVA - 624 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

题目大意:一个储存时间为n的空白磁带,从m首歌曲中选择几首,尽可能的是空白磁带的空白时间最少,并按输入顺序输出选择的歌曲

解题思路:0-1背包,唯一的难点就是路径的输出,因为二维dp会记录路径,所以使用二维dp,定义dp[i][j]表示从第1首到第i首歌曲中选择若干首使得歌曲时间最长,则dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i]]+a[i]),那么怎么输出路径呢

我们先看下求dp数组的过程

		for(int i=1;i<=m;i++)//从1开始储存歌曲时间 
		{
			for(int j=0;j<=n;j++)
			{
				if(j<a[i])
				{
					dp[i][j]=dp[i-1][j];
				}
				else
					dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i]]+a[i]);
			}
				
		}

是从前往后更新dp数组,那么最后变化的那么数据就是我们需要的,因为最后一首歌曲和最长的时间dp[m][n]是最后一次变化的,如果dp[m][n]==dp[m-1][n-a[m]]+a[m],则说明最后一首歌曲一定是选中的一个,使其保存在ans[]数组中,然后在求dp[m-1][j-a[m]是从那一步来的,所以我们就可以从后往前推出路径,注意这种推导 ans[]保存的数是从后往前的,逆向输出就行了,理解这一点,这道题就好做了

AC代码:

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e5;
int a[22],dp[22][maxn],ans[22];
int main()
{
	int n,m;
	while(cin>>n)
	{
		memset(dp,0,sizeof(dp));
		cin>>m;
		for(int i=1;i<=m;i++)
			cin>>a[i];
		for(int i=1;i<=m;i++)
		{
			for(int j=0;j<=n;j++)
			{
				if(j<a[i])
				{
					dp[i][j]=dp[i-1][j];
				}
				else
					dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i]]+a[i]);
			}
				
		}
		int cnt=0;
		for(int i=m,j=n;i>0;i--)//逆向求输出序列 
		{
			if(j-a[i]>=0&&dp[i][j]==dp[i-1][j-a[i]]+a[i])//说明当前第i个元素是所求 
			{
				ans[cnt++]=a[i];
				j-=a[i];
			} 
		}
		for(int i=cnt-1;i>=0;i--)//求出的结果是方向的,所以从后往前输出 
		{
			cout<<ans[i]<<' ';
		}
		cout<<"sum:"<<dp[m][n]<<endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40707370/article/details/87706830
cd