week11作业选做题(0 1背包问题)

11-1(东东与ATM)

题意

一家银行计划安装一台用于提取现金的机器。
机器能够按要求的现金量发送适当的账单。
机器使用正好N种不同的面额钞票,例如D_k,k = 1,2,…,N,并且对于每种面额D_k,机器都有n_k张钞票。
例如,
N = 3,
n_1 = 10,D_1 = 100,
n_2 = 4,D_2 = 50,
n_3 = 5,D_3 = 10
表示机器有10张面额为100的钞票、4张面额为50的钞票、5张面额为10的钞票。
东东在写一个 ATM 的程序,可根据具体金额请求机器交付现金。
注意,这个程序计算程序得出的最大现金少于或等于可以根据设备的可用票据供应有效交付的现金。

输入格式:
程序输入来自标准输入。 输入中的每个数据集代表特定交易,其格式为:Cash N n1 D1 n2 D2 … nN DN其中0 <= Cash <= 100000是所请求的现金量,0 <= N <= 10是 纸币面额的数量,0 <= nk <= 1000是Dk面额的可用纸币的数量,1 <= Dk <= 1000,k = 1,N。 输入中的数字之间可以自由出现空格。 输入数据正确。
输出格式:
对于每组数据,程序将在下一行中将结果打印到单独一行上的标准输出中。

样例:
input:
735 3  4 125  6 5  3 350
633 4  500 30  6 100  1 5  0 1
735 0
0 3  10 100  10 50  10 10
output:
735
630
0
0

思路

  • 这是一个明显的多重背包问题,其思路与完全背包类似,区别在于完全背包的物品有V/Wi个,而多重背包有Ci个,其状态方程为:
    在这里插入图片描述
    fi,j 为前i件物品放进背包后的价值;
  • 我们进行一个优化,使用进制的思想将Ci进行一个二进制拆分,转化为0-1背包问题;进行这个优化的意义就是降低时间复杂度,为O(NVlogT);
  • 在此题中,我们没有看到关于质量的限制,反而看到了价值的限制,那么此时我们采取的操作就是质量等于价值,这样操作,就可以满足0-1背包的形式了。

总结

此题将价值同等于背包问题的质量,然后价值限制转化为质量限制;

代码

#include<iostream>
#include<algorithm>
#include<cstdio>

using namespace std;
int value[1010],C[1010];
int value1[150];
int f[100010];
int main()
{
	int cash;
	int N;
	while (cin>>cash>>N)
	{
		for (int i = 1; i <= N; i++)
		{
			cin>>C[i]>>value[i];
		}
		//进行二进制拆分
		int cnt = 0;
		for (int i = 1; i <=N; i++)
		{
			int t = C[i];
			for (int k = 1; k <= t; k <<= 1)
			{
				cnt++;
				value1[cnt] = k * value[i];
				t = t - k;
			}
			if (t > 0)
			{
				cnt++;
				value1[cnt] = t * value[i];
			}
		}
		
		for (int i = 0; i <= 100010; i++)
			f[i] = 0;
		for (int i = 1; i <= cnt; i++) {
			for (int j = cash; j >=0; j--)
			{
				if (j - value1[i] >= 0)
					f[j] = max(f[j], f[j - value1[i]] + value1[i]);
			}
		}
		cout << f[cash] << endl;
	}
	return 0;
}

11-2(东东开车了)

题意

东东开车出去泡妞(在梦中),车内提供了 n 张CD唱片,已知东东开车的时间是 n 分钟,他该如何去选择唱片去消磨这无聊的时间呢

假设:

CD数量不超过20张
没有一张CD唱片超过 N 分钟
每张唱片只能听一次
唱片的播放长度为整数
N 也是整数
我们需要找到最能消磨时间的唱片数量,并按使用顺序输出答案(必须是听完唱片,不能有唱片没听完却到了下车时间的情况发生)

本题是 Special Judge

输入格式:
多组输入
每行输入第一个数字N, 代表总时间,第二个数字 M 代表有 M 张唱片,后面紧跟 M 个数字,代表每张唱片的时长 例如样例一: N=5, M=3, 第一张唱片为 1 分钟, 第二张唱片 3 分钟, 第三张 4 分钟
所有数据均满足以下条件:
N≤10000
M≤20
输出格式:
输出所有唱片的时长和总时长,具体输出格式见样例

样例:
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
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

思路

  • 这个题就是一个普通的0-1背包问题,但是需要记录路径,之前学习到,使用滚动数组解决这个背包问题,但是不能去打印转态,所以我们这里不应该使用滚动数组,采用普通的方法解决即可;
  • 对于记录路径,我们采用递归的方式解决这个问题,通过对F[i][j]的分析,我们对于每张唱片是否加入背包进行一个标记,如果加入了,标记为1,然后进行下一层递归,如果没有,标记为0 ,进行下一层递归,最后,输出唱片中被标记为1的即为答案;

总结

此题依旧是没有0-1背包中的质量限制,我们同样是使用价值等于质量,然后将价值限制转化为质量限制;

代码

#include<iostream>
#include<algorithm>
using namespace std;
int  f[10001][10001];
int v[21];
int path[21];
void findWhat(int i, int j)
{
	if (i > 0)
	{
		if (f[i][j] ==f[i - 1][j])//相等说明没有装
		{
			path[i] = 0;
			findWhat(i - 1, j);
		}
		else if (f[i][j] == f[i - 1][j - v[i]] + v[i])//表示装了第i件物品
		{
			path[i] = 1;
			findWhat(i - 1, j - v[i]);//回到装包前的位置
		}
	}
}
int main()
{
	int n;
	while (cin >> n)
	{
		int m;
		cin >> m;
		for (int i = 0; i <= n; i++) {
			f[0][i] = 0;
			
		}
		for (int i = 1; i <=m; i++)
			cin >> v[i],path[i] = 0;
		int cnt = 0;
		for (int i = 1; i <= m; i++)
		{
			for (int j = 0; j <= n; j++)
			{
				f[i][j] = f[i - 1][j];
				if (j - v[i] >= 0)
				{
					f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + v[i]);
					
				}
			}
		}

		findWhat(m, n);
		for (int i = 1; i <= m; i++)
		{
			if (path[i] == 1)
			cout << v[i] << " ";
		}
		cout << "sum:" << f[m][n]<<endl;
	}
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值