洛谷普及组训练:动态规划(1) P1060 开心的金明

P1060 开心的金明

0-1背包问题典例,下面这个链接中总结了0-1背包问题的各种模板,在第一个例子中给出了优化。这一题类似于coin change里面的第二个方法。
https://www.luogu.org/problemnew/solution/P1060

题目描述
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NN元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的NN元。于是,他把每件物品规定了一个重要度,分为55等:用整数1-51−5表示,第55等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过NN元(可以等于NN元)的前提下,使每件物品的价格与重要度的乘积的总和最大。

请你帮助金明设计一个满足要求的购物单。

输入格式
第一行,为22个正整数,用一个空格隔开:N mNm(其中N(<30000)N(<30000)表示总钱数,m(<25)m(<25)为希望购买物品的个数。)

从第22行到第m+1m+1行,第jj行给出了编号为j-1j−1的物品的基本数据,每行有22个非负整数v pvp(其中vv表示该物品的价格(v \le 10000)(v≤10000),pp表示该物品的重要度(1-51−5)

输出格式
11个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)(<100000000)。

输入输出样例
输入 #1复制
1000 5
800 2
400 5
300 5
400 3
200 2
输出 #1复制
3900
一开始的思路

这种想法的思想是买的东西是可以无限选的,也就是说同一个东西可以买无数个,并且是求最值问题,组合直接不会产生重复。但是后来看了一下题意,题目希望的是一个0-1的背包问题;也就是说他们买的东西的个数是0或者1.

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct obj {
	int v;
	int w;
};

int main() {
	int dp[30001];
	int N, m;
	cin >> N >> m;
	obj list[26];
	for (int i = 1; i <= m; i++) {
		cin >> list[i].v >> list[i].w;
	}
	dp[0] = 0;
	memset(dp, 0, sizeof(dp));
	int tempmax=0;
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= m; j++) {
			int index = i - list[j].v;
			if (index>= 0) {
				dp[i]= max(dp[index] + list[j].v*list[j].w, dp[i]);
			}
		}
	}
	cout << dp[N];
	return 0;
}
正常的0-1背包问题模板
//0-1背包模板,未优化
//dp[i][j],     1<=i<=m,  1<=j<=N
for( int i =1;i<=m;i++){
   		for( int j=1; j<=N;j++){
   			if( j>=v)
			dp[i][j]=max(dp[i-1][j],dp[i-1][ j- v]+w;
			else
			dp[i][j]=dp[i-1][j];
		}
}
//0-1背包一维数组优化模板
//dp[j],      N>=j>=1,  m>=i>=1
for( int i=1; i<=m;i++){
	for( int j=N; j>=0; j--){
	if(j>=list[i].v)
	dp[j]=max( dp[j], dp[j-list[i].v]+list[i].v*list[i].w);
	//else dp[j]=dp[j];//这里可以省略不写
	}
}

本题代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct obj {
	int v;
	int w;
};

int main() {
	int** dp = new int*[30];
	for (int i = 0; i < 30; i++) {
		dp[i] = new int[30001];
		memset(dp[i], 0, sizeof(int)*30001);
	}

	obj list[30];
	int N, m;
	cin >> N >> m;
	for (int i = 1; i <= m; i++) {
		cin >> list[i].v >> list[i].w;
	}
	for (int i = 1; i <= m; i++) {
		for (int j = 0; j <= N; j++) {
			if (j >= list[i].v)
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - list[i].v] + list[i].v*list[i].w);
			else
				dp[i][j] = dp[i - 1][j];
		}
	}
	cout << dp[m][N];
	for (int i = 0; i < 30; i++) {
		delete[]dp[i];
	}
	delete[]dp;
	return 0;
}
一维数组优化后的结果
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct obj {
	int v;
	int w;
};

int main() {
	int dp[30001] = {0};
	obj list[30];
	int N, m;
	cin >> N >> m;
	for (int i = 1; i <= m; i++) {
		cin >> list[i].v >> list[i].w;
	}
	for (int i = 1; i <= m; i++) {
		for (int j = N; j >=0; j--) {
			if (j >= list[i].v)
				dp[j] = max(dp[j], dp[j - list[i].v] + list[i].v*list[i].w);
		}
	}
	cout << dp[N];
	return 0;
}
使用vector创建dp矩阵
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
struct obj {
	int v;
	int w;
};
int main() {
	int N, m;
	cin >> N >> m;
	obj list[26];
	for (int i = 1; i <= m; i++) {
		cin >> list[i].v >> list[i].w;
	}
	vector<vector<int>> dp(m + 1, vector<int>(N + 1));//dp[i][j]可以买前i种商品,恰好花j元
	for (int i = 0; i <= N; i++) {
		dp[0][i] = 0;
	}
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= N; j++) {
			if (j >= list[i].v)
				dp[i][j] = max({ dp[i - 1][j],dp[i - 1][j - list[i].v] + list[i].v*list[i].w });
			else dp[i][j] = dp[i - 1][j];
		}
	}

	cout<<dp[m][N];
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值