本题做法
- 0-1 背包 DP。
思路
这题就是简单的 0-1 背包 DP 的小小变形而已。
我们可以定义 \(dp[i][j]\) 为前 \(i\) 个木石中剩余体力为 \(j\) 的最大填补体积。使用双重循环来递推 \(dp\)。若 \(j\ge m_i\),则 \(dp[i][j]=\max(dp[i-1][j],dp[i-1][j-m_i]+k_i)\);否则 \(dp[i][j]=dp[i-1][j]\)。
全部计算完毕后,若 \(dp[n][c]<v\),则代表即使用光所有体力,也没法衔来足够体积的木石,则输出 Impossible
;否则从 \(c\) 反向遍历到 \(0\),若首次出现 \(dp[n][i]<v\),则代表 \(dp[n][i+1]\ge v\),则剩余的体力就是 \(c-i-1\),输出 \(c-i-1\)。
但是我写代码的时候认为 \(dp\) 开二维数组会爆内存(因为 \(1\le n\le 10^4\)),所以我用滚动数组优化了一下,此时第二层循环需要倒序循环,否则会覆盖上一个木石的数据。
代码
#include<bits/stdc++.h>typedef long long ll;
typedef unsigned long long ull;using namespace std;const int N=1e4+5;struct wood{ll v,w;
} w[N];ll n,m,c,dp[N];
//dp[i]表示在剩下i体力的时候int main(){cin>>m>>n>>c;for(int i=1;i<=n;i++) {cin>>w[i].w>>w[i].v;}for(int i=1;i<=n;i++) {for(int j=c;j>=0;j--) {if(j>=w[i].v) {dp[j]=max(dp[j],dp[j-w[i].v]+w[i].w);}}}if(dp[c]<m) cout<<"Impossible"<<endl;else{for(int i=c;i>=0;i--){if(dp[i]<m) {cout<<c-i-1<<endl;return 0;}}}return 0;
}