洛谷 P1048 [NOIP 2005 普及组] 采药 题解

题目大意

洛谷 P1048 [NOIP 2005 普及组] 采药

给定数组 \(v_{[1..n]}\)\(w_{[1..n]}\),从中选取任意个下标 \(p_{[1..m]}\) 使 \(\sum_{i=1}^m{v_{p_i}}\le t\)\(\sum_{i=1}^m{w_{p_i}}\) 最大。求这个最大值。

思路分析

0-1 背包模板题。

Task 1

定义 \(dp_{i,j}\) 表示前 \(i\) 个元素,\(v\) 总和不超过 \(j\) 的最大 \(w\) 之和。则对于第 \(i\) 个元素,可以选择不取,那么 \(w\) 之和为 \(dp_{i-1,j}\);若选择取,则剩下的 \(i-1\) 个元素的 \(v\) 总和为 \(j-v_i\),所以此时 \(w\) 之和为 \(dp_{i-1,j-v_i}+w_i\)。状态转移方程为:

\[dp_{i,j}=\max(dp_{i-1,j},dp_{i-1,j-v_i}+w_i) \]

滚动数组

注意到 \(dp_{i,j}\) 只与 \(dp_{i-1,[0..j-1]}\) 有关,所以可以只开一维数组,压掉 \(i\) 这一维,节省空间。但是由于前文所讲,状态转移要用到下标更小的元素,所以 \(j\) 应倒序遍历。

代码呈现

#include<bits/stdc++.h>
using namespace std;

const int N=105,T=1010;
int t,m;
int v[N],w[N],dp[T];

int main(){
    scanf("%d%d",&t,&m);
    for (int i=1;i<=m;++i) scanf("%d%d",v+i,w+i);
    for (int i=1;i<=m;++i){
        for (int j=t;j>=v[i];--j) dp[j]=max(dp[j],dp[j-v[i]]+w[i]); // 倒序遍历
    }
    printf("%d",dp[t]);
    return 0;
}
posted @ 2025-12-14 20:33  CodingJuRuo  阅读(1)  评论(0)    收藏  举报