洛谷P1510 精卫填海 题解

本题做法

  • 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;
}
posted @ 2025-07-29 19:23  2789617221guo  阅读(12)  评论(0)    收藏  举报