洛谷題單指南-動態規劃1-P1048 [NOIP2005 普及組] 採藥

江城伍月發表於2024-04-18

原題連結:https://www.luogu.com.cn/problem/P1048

題意解讀:在有限的時間內,採集到最大價值的草藥,實際上是01揹包問題,這裡的總時間就是揹包的體積。

解題思路:

設v[]表示每株草藥的時間, w[]表示每株草藥的價值,

dp[i][j]表示採前i株草藥在時間j內可得到的最大價值,
對於第i株草藥,採或不採,取max,可得遞推公式:dp[i][j] = max(dp[i][j], dp[i-1][j-v[i]] + w[i]);
注意i取值1~m,j取值0~t,要保證j-v[i]>=0才可採第i株

100分程式碼(二維):

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

const int N = 105, M = 1005;
int t, m;
int v[N], w[N]; //v:時間 w:價值
int dp[N][M]; //dp[i][j]表示採前i株草藥在時間j內可得到的最大價值

int main()
{
    cin >> t >> m;
    for(int i = 1; i <= m; i++) cin >> v[i] >> w[i];

    for(int i = 1; i <= m; i++)
    {
        for(int j = 0; j <= t; j++)
        {
            dp[i][j] = dp[i-1][j];
            if(j >= v[i])
                dp[i][j] = max(dp[i][j], dp[i-1][j-v[i]] + w[i]);
        }
    }
    cout << dp[m][t];
    return 0;
}

由於i只跟i-1有關,可以用一維陣列進行最佳化

又因為j透過更小的j-v[i]推出,如果在一維的情況下,先更新小的dp[j],會導致後面計算依賴的dp[j]發生變化,所有要先更新大的j,也就是j的遍歷要從大到小,另外可以把j >= v[i]直接提到for條件中

100分程式碼(一維):

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

const int N = 105, M = 1005;
int t, m;
int v[N], w[N]; //v:時間 w:價值
int dp[M]; //dp[j]表示在時間j內可得到的最大價值

int main()
{
    cin >> t >> m;
    for(int i = 1; i <= m; i++) cin >> 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]);
        }
    }
    cout << dp[t];
    return 0;
}

相關文章