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;
}