原题链接
题目大意
详情请进我的第一篇SSL1045博客
S a m p l e \mathbf{Sample} Sample I n p u t \mathbf{Input} Input
70 3
71 100//时间和价值
69 1
1 2
S a m p l e \mathbf{Sample} Sample O u t p u t \mathbf{Output} Output
3
H
i
n
t
&
E
x
p
l
a
i
n
\mathbf{Hint\&Explain}
Hint&Explain
取第二个和第三个草药,时间为69+1=70
,价值为2+1=3
。
解题思路
本题有新的一种方法:
滚
动
数
组
滚动数组
滚动数组。
顺推和逆推的思路我在第一篇已经讲过了,那么可以发现,我们在顺推使用二维数组推答案的时候,只用到了
d
p
i
,
j
dp_{i,j}
dpi,j和
d
p
i
−
1
,
j
dp_{i-1,j}
dpi−1,j,所以为何不能只存储这两个数组进行运算呢?
方法如下:
设
p
r
e
j
pre_j
prej为原来的
d
p
i
−
1
,
j
dp_{i-1,j}
dpi−1,j,
d
p
j
dp_j
dpj为原来的
d
p
i
,
j
dp_{i,j}
dpi,j。
- 将 d p dp dp数组里的数拷贝到 p r e pre pre数组中,即记录 d p i − 1 , j dp_{i-1,j} dpi−1,j。
- 把 p r e j pre_j prej当做 d p i − 1 , j dp_{i-1,j} dpi−1,j代入原来顺推时的状态转移方程就可推出答案。
而最后的答案就是循环过后的 d p m dp_{m} dpm,就是原来的 d p n , m dp_{n,m} dpn,m。
上代码
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
int w[110],c[110];
int dp[1010],pre[1010];
int main()
{
memset(dp,0,sizeof(dp));
cin>>m>>n;
for(int i=1; i<=n; i++) cin>>w[i]>>c[i];
for(int i=1; i<=n; i++)
{
memcpy(pre,dp,sizeof(dp));
for(int j=m; j>=w[i]; j--)
{
dp[j]=std::max(pre[j],pre[j-w[i]]+c[i]);
}
}
cout<<dp[m]<<endl;
return 0;
}
其他问题
既然可以做出最大价值,那么对应最大价值的当然就有一个最优的方案。
我不知道会不会有一些毒瘤好心帮助采药人的题目会让你输出采药的方案,所以这里还是说一下吧。
题
目
大
意
\Large{题目大意}
题目大意
基本和原题一样,但是多了一个输出要求:最优的方案。
S
a
m
p
l
e
\mathbf{Sample}
Sample
I
n
p
u
t
\mathbf{Input}
Input
70 3
71 100//时间和价值
69 1
1 2
S a m p l e \mathbf{Sample} Sample O u t p u t \mathbf{Output} Output
3
2 3
H
i
n
t
&
E
x
p
l
a
i
n
\mathbf{Hint\&Explain}
Hint&Explain
取第二个和第三个草药,时间为69+1=70
,价值为2+1=3
。
解 题 思 路 \Large{解题思路} 解题思路
用顺推二维
d
p
dp
dp先做出来结果,重要的部分就是最后的输出了。
先说方法:
- i i i从 1 ∼ n 1\sim n 1∼n循环,如果 d p i , m ≠ d p i − 1 , m dp_{i,m}\ne dp_{i-1,m} dpi,m=dpi−1,m,就输出 i i i。
for(int i=1; i<=n; i++)
if(dp[i][m]!=dp[i-1][m]) cout<<i<<" ";
cout<<endl;
原理:
从
i
i
i开始循环每一个物品,他对应的
d
p
i
,
m
dp_{i,m}
dpi,m只可能从
d
p
i
−
1
,
m
dp_{i-1,m}
dpi−1,m和
d
p
i
−
1
,
m
−
w
i
+
c
i
dp_{i-1,m-w_i}+c_i
dpi−1,m−wi+ci得来,所以如果
d
p
i
,
m
dp_{i,m}
dpi,m不变,那么就是从
d
p
i
−
1
,
m
dp_{i-1,m}
dpi−1,m推来的,即没有选
i
i
i物品。如果变了,就是从
d
p
i
−
1
,
m
−
w
i
+
c
i
dp_{i-1,m-w_i}+c_i
dpi−1,m−wi+ci推来,即选了
i
i
i物品。所以只要
d
p
i
,
m
dp_{i,m}
dpi,m和
d
p
i
−
1
,
m
dp_{i-1,m}
dpi−1,m不一样,就输出
i
i
i。
上 代 码 \Large{上代码} 上代码
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
int w[110],c[110];
int dp[110][1010];
int main()
{
memset(dp,0,sizeof(dp));
cin>>m>>n;
for(int i=1; i<=n; i++) cin>>w[i]>>c[i];
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
if(j>=w[i])
dp[i][j]=std::max(dp[i-1][j],dp[i-1][j-w[i]]+c[i]);
else
dp[i][j]=dp[i-1][j];
}
}
cout<<dp[n][m]<<endl;
for(int i=1; i<=n; i++)
if(dp[i][m]!=dp[i-1][m]) cout<<i<<" ";
cout<<endl;
return 0;
}
完美切题~