概要:
0/1ナップサック問題-動的計画法テンプレート
問題分析:
元のタイトルへのリンク:P1048医学の収集
双子の兄弟:完全なナップサック問題
アルゴリズム:
動的計画法は、問題を継続的に細分化し、大きな問題を小さな問題に分割し、それらを段階的に解決する方法です。後遺症がなく、最適な下部構造を満たします。動的計画法のアプリケーションの最も明白な特徴は、各サブ問題が1回だけ解決され、結果が解決後も保持されることです。次に同じ質問に遭遇したときに、直接答えを得ることができます。
0/1バックパック問題は、m個のアイテムがあり、各アイテムに対応する値があることを意味します。バックパックの容量がnを超えない場合は、最も価値のあるアイテムを運び、各アイテムは最大1回選択できます。選択された各項目の最大値は、最後に選択された後の合計値に関連しているため、問題は最適な部分構造を持ち、動的計画法によって解決できます。
具体的な状態遷移式については、公式ウェブサイトの質問応答LuoguP1048の薬の選択-質問応答を参照してください。
ここでは1つのポイントのみが強調されています。つまり、コードの内側のループは、大きいものから小さいものへと逆方向にトラバースする必要があります。それが小さいものから大きいもの、つまり1からnの場合、それは別の動的計画問題に対応します-完全なナップサック問題(1つの項目を複数回選択できます)
もう1つの詳細は、この質問を2次元配列、つまりans [m] [n]で実行できることです。ここで、mはアイテムの数、nは合計時間制限です。次に、任意の要素ans [i] [j]は時間長jの最初のi項目で得られる最大利益。
もちろん、問題を解決するには1次元配列を使用する方が効率的です。コードは基本的に同じですが、意味は2桁の配列ほど明白ではありません。つまり、ans [n] ans [i]の各要素は時間はiであるという前提の下で、得られる最大利益はn項目から任意に選択できます。。
ただし、1次元であろうと2次元であろうと、状態遷移方程式に従って継続的に更新して最適値を取得することが重要です。
コードと詳細なコメント:
方法1:1次元配列を使用する
#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;
#pragma warning(disable:4996)
class Solution {
public:
int n, m;
vector<pair<int, int>> w;
vector<long long int> ans;
void dynamic_programming() {
cin >> n >> m;
w.resize(m + 1);
ans.resize(n + 1,0);
for (int i = 1; i <= m; ++i)
cin >> w[i].first >> w[i].second;
for (int i = 1; i <= m; ++i)
{
for (int j = n; j >0; --j)
{
if (j >= w[i].first)
ans[j] = max(ans[j], ans[j - w[i].first] + w[i].second);
}
/*for(int i=1;i<=n;++i)
cout << ans[i] << " ";
cout << endl;*/
}
cout << ans[n]<<endl;
}
};
int main() {
//freopen("in.txt", "r", stdin);
Solution s;
s.dynamic_programming();
return 0;
}
方法2:2次元配列を解く:
#include <iostream>
#include <stdio.h>
#include <vector>
#pragma warning(disable:4996)
using namespace std;
class Solution {
public:
int n, m; //n是总时间 ,m是药品的件数
vector<pair<int,int>> w;
vector<vector<int>> ans; //利用二维网格搜索
void dynamic_programming() {
cin >> n >> m;
w.resize(m + 1);
ans.resize(m + 1, vector<int>(n+5, 0));
for (int i = 1; i <= m; ++i) {
int x, y;
cin >> x >> y;
w[i] = {
x,y };
}
for (int i = 1; i <= m; ++i) {
for (int j = 1; j<=n; ++j) {
if (j >= w[i].first)
ans[i][j] = max(ans[i - 1][j], ans[i - 1][j - w[i].first] + w[i].second);
else
ans[i][j] = ans[i - 1][j];
}
}
cout << ans[m][n];
}
};
int main()
{
//freopen("in.txt", "r", stdin);
Solution s;
s.dynamic_programming();
return 0;
}