3.3 B 无聊的葱神

题目描述

葱神喜欢K题,但在无限的K题生涯中总会出现特别无聊的日子。这种时候葱神就会通过为自己加上BGM来驱散无聊的情绪。作为一个特别看重K题手感的人,葱神拒绝通过在电脑上打开网易云音乐来听歌,这非常影响手感!取而代之的是,他通过磁带播放器来听歌。每当一个无聊的日子出现的时候,葱神就会找出一盘能录制N分钟音乐的磁带和M首歌。挑剔的葱神不会听两遍同一首歌,也不会一首歌听一半,现在你要编写程序帮助葱神挑选歌曲使得磁带剩下的空白时间最少。

 


数据约束:
N<65535 M<20
歌曲长度不超过N

输入

第一行为一个整数T,表示无聊的日子的数量
接下来T行,每行:
第一个数字N,代表磁带的容量,第二个数字M,代表歌曲的数量,随后M个数字,分别是这些歌曲的长度

输出

每一个无聊的日子对应两行
第一行为被选中的歌曲的长度(按输入顺序从后向前排列),简单起见最后一个数字后面有一个空格
第二行为最优解的歌曲总长

 

样例输入

5
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 40 12 9 8 2

样例输出

4 1
5
2 8
10
4 5 10
19
7 5 4 3 2 1 23 10
55
2 8 9 12 10 4
45

解题思路

先是常规的01背包,不压维,把磁带容量n作为背包容量,物品价值和花费都为歌曲长度。然后求01背包,得到最优歌曲总长。至于路径记录,可以用dfs。从dp[n][m]开始搜索,回忆一下求背包的过程,如果dp[n - 1][m] == dp[n][m],那么就说明第n件物品没有被选择(其实也可能被选择,但是当它没选肯定可以),那么就搜索到dp[n - 1][m],如果dp[n - 1][m] != dp[n][m],那就说明第n件物品被选择了,就输出这首歌的长度,然后搜索到dp[n - 1][m - v[n]]。接下来的同理,一直到n或者m等于0了,不能再往下找了,结束递归。其实就是一个逆过程。

代码如下

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#define maxn 75535
using namespace std;
int dp[25][maxn];
int v[25];  //花费and价值 
void dfs(int x, int y)
{
	if(x == 0 || y == 0)
		return;
	if(dp[x][y] == dp[x - 1][y])  //相等说明没有选择x 
		dfs(x - 1, y);
	else {   //否则说明选择了 
		cout << v[x] << " ";
		dfs(x - 1, y - v[x]);
	}	
}
int main()
{
	int t;
	scanf("%d", &t);
	memset(dp, 0, sizeof(dp));
	while(t --){
		int n, m;
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= m; i ++)
			scanf("%d", &v[i]);
		for(int i = 1; i <= m; i ++){  //求01背包 
			for(int j = 1; j <= n; j ++){
				if(j - v[i] >= 0)
					dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + v[i]);
				else
					dp[i][j] = dp[i - 1][j];
			}
		}
		dfs(m, n);
		cout << endl;
		cout << dp[m][n] << endl;   
	}
	return 0;
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值