小梁来到了伽勒尔地区并参加了联盟赛热身赛,比赛小岛上有n个精灵散落在岛上各处,她有一个大小为s的背包,每个精灵的战斗值为vi,体积为wi。
请问在她临走之前背包内宝可梦的战斗力总和最多为多少,并输出其战斗值总和sum以及背包内的精灵数量ans。
示例1
输入
1
5 5
1 3
2 5
1 2
4 2
6 1
输出
10 3
背包问题,问了同学他说好像是多重背包?
赛后搜得:背包问题详解
这题恶心的地方是用dp求出最大的价值后还要让你计数,问放进去了多少个精灵球,卡了非常久。
dp部分:
我原本将这个计数的设为是cnt[i][j]=cnt[i-1][j];
然后if判断是否放入,放入则cnt[i][j]++
。这样是错误的,因为价值更新的话应该是前面那个容量的背包的次数再加一,而不是简单的++。
应该改为
cnt[i][j]=cnt[i-1][j];
//.....
cnt[i][j]=cnt[i-1][j-g[i].w]+1;
正解:
for(int i=1;i<=n;i++){
for(int j=0;j<=s;j++){
dp[i][j]=dp[i-1][j];
cnt[i][j]=cnt[i-1][j];
if(j>=g[i].w){
if(dp[i-1][j-g[i].w]+g[i].v>dp[i-1][j]){
dp[i][j]=dp[i-1][j-g[i].w]+g[i].v;
cnt[i][j]=cnt[i-1][j-g[i].w]+1;
}
}
}
}
完整代码:
#include
using namespace std;
const int N=1e4+5;
struct gg{
int v,w;
}g[N];
int dp[N][N];
int cnt[N][N];
void solve()
{
int n,s;
cin>>n>>s;
for(int i=1;i<=n;i++)cin>>g[i].w>>g[i].v;
for(int i=1;i<=n;i++){
for(int j=0;j<=s;j++){
dp[i][j]=dp[i-1][j];
cnt[i][j]=cnt[i-1][j];
if(j>=g[i].w){
if(dp[i-1][j-g[i].w]+g[i].v>dp[i-1][j]){
dp[i][j]=dp[i-1][j-g[i].w]+g[i].v;
cnt[i][j]=cnt[i-1][j-g[i].w]+1;
}
}
}
}
cout<>t;
while(t--)solve();
return 0;
}
dp仍然是我比较不熟练的部分,需要多做相关的题目。
6/21 优化
经同学提议优化了一下,将数组开成1维,节省了非常多的空间。
需要注意的是枚举s的时候需要从后往前,不能从前往后,否则会变成完全背包(每个精灵有无数只),而本题需要的是01背包(每个精灵只有一只)。
#include
using namespace std;
const int N=1e4+5;
struct gg{
int v,w;
}g[N];
int dp[N];
int cnt[N];
void solve()
{
memset(dp,0,sizeof(dp));
memset(cnt,0,sizeof(cnt));
int n,s;
cin>>n>>s;
for(int i=1;i<=n;i++)cin>>g[i].w>>g[i].v;
for(int i=1;i<=n;i++){
for(int j=s;j>=g[i].w;j--){//从s开始
if(dp[j-g[i].w]+g[i].v>dp[j]){
dp[j]=dp[j-g[i].w]+g[i].v;
cnt[j]=cnt[j-g[i].w]+1;
}
}
}
cout<>t;
while(t--)solve();
return 0;
}