week11 F-东东开车辣

题目

样例输入

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

样例输出

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 ]记录前 i 个物品放进容量为 j 的背包中的最大价值。

状态转移方程为

我觉得,这道题关键问题在于路径记录。

路径记录

法一:通过回退寻找路径,如果f[ i ][ cash ] == f[ i - 1 ][ cash ],说明没有用到第 i 张CD,直到回退到f[1]。

法二:申请二维bool数组,在计算 f 数组的时候,如果在f[ i ][ j ]时采用第 i 张CD,则记录到bool数组中,进行回退。

两个方法计算方法答案可能有所不同(在出现多组答案时),将两组代码都po出来,都可以过oj(为什么写法二呢,因为法一对不上样例,我是写完法二才发现原来法一早就过了oj...)。

 

代码

法一

#include<iostream>
#include<algorithm>
using namespace std;

int N, M;
int w[22];
int packet[25][10010];
bool is[22];

void traceback()
{
	int c = N;
	for (int i = M; i >= 1; i--)
	{
		if (packet[i][c] == packet[i - 1][c]) is[i] = 0;
		else
		{
			is[i] = 1;
			c -= w[i];
		}
	}
	for (int i = 1; i <= M; i++)
		if (is[i]) cout << w[i] << " ";
}


int main()
{
	while (cin >> N >> M)
	{
		for (int i = 1; i <= M; i++)
			cin >> w[i];

		for (int i = 0; i <= N; i++) packet[0][i] = 0;
		
		for (int i = 1; i <= M; i++)
		{
			for (int j = 0; j <= N; j++)
			{
				packet[i][j] = packet[i - 1][j];
				if (j - w[i] >= 0)
					packet[i][j] = max(packet[i][j], packet[i - 1][j - w[i]] + w[i]);
			}		
		}

		traceback();
		cout << "sum:" << packet[M][N] << endl;
			
	}
}

 

法二

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

int N, M;
int w[22];
int packet[25][10010];
bool is[22];
bool iss[25][10010];

void traceback()
{
	int c = N;
	for (int i = M; i >= 1; i--)
	{
		if (!iss[i][c]) is[i] = 0;
		else
		{
			is[i] = 1;
			c -= w[i];
		}
	}
	for (int i = 1; i <= M; i++)
		if (is[i]) cout << w[i] << " ";
}


int main()
{
	while (cin >> N >> M)
	{
		memset(iss, 0, sizeof iss);
		memset(is, 0, sizeof is);
		for (int i = 1; i <= M; i++)
			cin >> w[i];

		for (int i = 0; i <= N; i++) packet[0][i] = 0;
		
		for (int i = 1; i <= M; i++)
		{
			for (int j = 0; j <= N; j++)
			{
				packet[i][j] = packet[i - 1][j];
				if (j - w[i] >= 0 && packet[i - 1][j - w[i]] + w[i] >= packet[i][j])
				{
					packet[i][j] = packet[i - 1][j - w[i]] + w[i];
					iss[i][j] = 1;
				}
					
			}		
		}

		traceback();
		cout << "sum:" << packet[M][N] << endl;
			
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值