バックパック問題(01バックパック完全バックパック多重バックパック)
30611 ワード
今日は牛客網のテーマを使って、リュックサックの問題の解を使います.ここでリュックの問題をまとめてみます.
リュックサックの問題は主に以下の3種類に分けられます. 01リュック 完全リュック 多重リュック 01リュックサックの問題
N個の品物とV容量のリュックサックがあります.i番目の品物の価格(すなわち体積、以下同)はw[i]、価値はc[i]である.どの品物をリュックサックに入れると、これらの品物の費用の合計がリュックサックの容量を超えず、価値の合計が最大になるかを解く.
これは私たちが最もよく出会うリュックサックの問題で、実は状態移動方程式を明らかにすればいいだけです.
この方程式を説明します.の場合一:i番目は入れないが、この場合得られる価値は:f[i-1][v] である.ケース2:i番目を入れると、f[i-1][v-c[i]+w[i] が得られます.
例を挙げて理解を深めましょう.ブルーブリッジカップの練習中の採薬問題です.
辰辰は天資聡明な子供で、彼の夢は世界で最も偉大な医者になることです.そのため、彼は近くで最も威信のある医者を師にしたいと思っています.医者は彼の資質を判断するために,彼に難題を出した.医者は彼を薬草だらけの洞窟に連れて行って言った.「子供、この洞窟にはいくつかの異なる薬草があります.それぞれの株を採るには時間がかかります.それぞれの株にも価値があります.私はあなたにしばらくの時間をあげます.この時間の中で、あなたはいくつかの薬草を採ることができます.もしあなたが賢い子供であれば、採った薬草の総価値を最大にすることができます.」もしあなたが辰辰だったら、あなたはこの任務を完成することができますか?入力記述Input Description入力1行目には2つの整数T(1<=T<=1000)とM(1<=M<=100)があり、1つのスペースで区切られており、Tは合計で採薬できる時間を表し、Mは洞窟内の薬草の数を表す.次のM行は各行に1~100の2つを含む(1と100を含む)の整数は、ある薬草を採取する時間とその薬草の価値をそれぞれ表す.出力記述Output Description出力は、所定の時間内に採取可能な薬草の最大総価値を示す1行のみを含む.Sample Input 70 3 71 100 69 1 2サンプルはSample Output 3データ範囲と提示Data Siを出力するze&Hint【データ規模】30%のデータに対して、M<=10;すべてのデータに対して、M<=100.
一次元配列を用いて問題を解き,状態圧縮後,f[v]をv kgを超えない最大値とすると,f[v]=max(f[v],f[v-w[i]+c[i]),v>=w[i],1<=i<=nとなる.
完全なリュックサック問題
N種類の品物とV容量のリュックサックがあり、それぞれの品物には無限のものがあります.第i種の物品の費用はw[i]、価値はc[i]である.リュックサックにどのアイテムを入れるかを解くと、これらのアイテムの費用の合計がリュックサックの容量を超えず、価値の合計が最大になります.
完全リュックと01リュックはよく似ていて、違いは完全リュックの品物が無限にあることです.前の選択から選択しないかを選択しないかに変えて、いくつかを選択します.
01リュックサックと同じように、状態遷移方程式を書くことができます.
もう一つ簡単な最適化↓_↓
ある品物の価値が別の品物の価値より小さいが、価格が別の品物より高い場合、私たちはこの品物を考えなくてもいい.すなわち、2つの物品i,jがc[i]<=c[j]を満たし、w[i]>=w[j]であれば、物品jを除去し、考慮しない.私たちはどうして高くてまずいものを買うのですか(′▽`)
多重リュックサック問題
N種類の品物とV容量のリュックサックがあります.第i種の物品は最大n[i]件が利用可能であり、1件当たりの費用はw[i]、価値はc[i]である.リュックサックにどのアイテムを入れるかを解くと、これらのアイテムの費用の合計がリュックサックの容量を超えず、価値の合計が最大になります.
ここにはもう一つの制限条件があり、各品物が利用可能な回数を規定しています.
同様に,状態遷移方程式を導くことができる:f[i][v]=max(f[i−1][v−kw[i]+kc[i]|0<=k<=n[i])
【問題の説明】
クラスが学校の運動会で全校1位の成績を収めたことを祝うため、担任は祝賀会を開くことを決め、そのために賞品を買って選手をねぎらうことにした.最大価値の賞品を購入できる金額を期待し、精力と体力を補うことができます.
【入力形式】
1行目の2つの数n(n<=500)、m(m<=6000)で、nは購入したい賞品の数を表し、mは送金金額を表す.次にn行は、各行3個分、v,w,sは、それぞれI番目の賞品の価格、価値(価格と価値が異なる概念)と購入数(0個からs個まで可能)を表し、そのうちv<=100、w<=1000、s<=10である.
【出力形式】
1行目:1つの数で、今回の購入で得られる最大の価値を表します(注意!価格ではありません).
【入力サンプル】
5 1000
80 20 4
40 50 9
30 50 7
40 30 6
20 20 1
【出力サンプル】
1040
dp圧縮後
TIP:ちょうどいっぱいなら二次元配列f 0][0]は0に設定され、その他は-INFに設定されている. 次元配列f[0]は0であり、その他は-INFである.
このブログの説明を復習してください.http://www.cnblogs.com/fengziwei/p/7750849.html
リュックサックの問題は主に以下の3種類に分けられます.
N個の品物とV容量のリュックサックがあります.i番目の品物の価格(すなわち体積、以下同)はw[i]、価値はc[i]である.どの品物をリュックサックに入れると、これらの品物の費用の合計がリュックサックの容量を超えず、価値の合計が最大になるかを解く.
これは私たちが最もよく出会うリュックサックの問題で、実は状態移動方程式を明らかにすればいいだけです.
f[i][v] = max(f[i-1][v], f[i-1][v-w[i]]+c[i])
この方程式を説明します.
例を挙げて理解を深めましょう.ブルーブリッジカップの練習中の採薬問題です.
辰辰は天資聡明な子供で、彼の夢は世界で最も偉大な医者になることです.そのため、彼は近くで最も威信のある医者を師にしたいと思っています.医者は彼の資質を判断するために,彼に難題を出した.医者は彼を薬草だらけの洞窟に連れて行って言った.「子供、この洞窟にはいくつかの異なる薬草があります.それぞれの株を採るには時間がかかります.それぞれの株にも価値があります.私はあなたにしばらくの時間をあげます.この時間の中で、あなたはいくつかの薬草を採ることができます.もしあなたが賢い子供であれば、採った薬草の総価値を最大にすることができます.」もしあなたが辰辰だったら、あなたはこの任務を完成することができますか?入力記述Input Description入力1行目には2つの整数T(1<=T<=1000)とM(1<=M<=100)があり、1つのスペースで区切られており、Tは合計で採薬できる時間を表し、Mは洞窟内の薬草の数を表す.次のM行は各行に1~100の2つを含む(1と100を含む)の整数は、ある薬草を採取する時間とその薬草の価値をそれぞれ表す.出力記述Output Description出力は、所定の時間内に採取可能な薬草の最大総価値を示す1行のみを含む.Sample Input 70 3 71 100 69 1 2サンプルはSample Output 3データ範囲と提示Data Siを出力するze&Hint【データ規模】30%のデータに対して、M<=10;すべてのデータに対して、M<=100.
#include
using namespace std;
int dp[101][1001];
int main()
{
int t, m;
cin >> t >> m;
for(int i = 1; i <= m; i++)
{
int a, b;
cin >> a >> b;
for(int j = 1; j <= t; j++)
{
if(j >= a)
dp[i][j] = max(dp[i-1][j], dp[i-1][j-a] + b);
else
dp[i][j] = dp[i-1][j];
}
}
cout << dp[m][t];
return 0;
}
一次元配列を用いて問題を解き,状態圧縮後,f[v]をv kgを超えない最大値とすると,f[v]=max(f[v],f[v-w[i]+c[i]),v>=w[i],1<=i<=nとなる.
#include
#include
using namespace std;
const int maxm = 2001, maxn = 101;
int m, n;
int w[maxn], c[maxn];
int f[maxm];
int main()
{
scanf("%d%d",&m, &n); // m n
for (int i=1; i <= n; i++)
scanf("%d%d",&w[i],&c[i]); //
for (int i=1; i <= n; i++) // f(v) v
for (int v = m; v >= w[i]; v--) //
f[v] = max(f[v-w[i]]+c[i], f[v]);
printf("%d
",f[m]); // f(m)
return 0;
}
完全なリュックサック問題
N種類の品物とV容量のリュックサックがあり、それぞれの品物には無限のものがあります.第i種の物品の費用はw[i]、価値はc[i]である.リュックサックにどのアイテムを入れるかを解くと、これらのアイテムの費用の合計がリュックサックの容量を超えず、価値の合計が最大になります.
完全リュックと01リュックはよく似ていて、違いは完全リュックの品物が無限にあることです.前の選択から選択しないかを選択しないかに変えて、いくつかを選択します.
01リュックサックと同じように、状態遷移方程式を書くことができます.
f[i][v]=max(f[i-1][v-k*c[i]]+k*w[i] | 0<=k*c[i]<=v)
もう一つ簡単な最適化↓_↓
ある品物の価値が別の品物の価値より小さいが、価格が別の品物より高い場合、私たちはこの品物を考えなくてもいい.すなわち、2つの物品i,jがc[i]<=c[j]を満たし、w[i]>=w[j]であれば、物品jを除去し、考慮しない.私たちはどうして高くてまずいものを買うのですか(′▽`)
#include
#include
using namespace std;
const int maxm=2001,maxn=101;
int n,m,v,i;
int c[maxn],w[maxn];
int f[maxm];
int main()
{
scanf("%d%d",&m,&n); // m n
for(i=1;i<=n;i++)
scanf("%d%d",&w[i],&c[i]);
for(i=1;i<=n;i++)
for(v=w[i]; v<=m; v++) // f[v] v
// v++ 01
f[v]=max(f[v-w[i]]+c[i], f[v]);
printf("%d
", f[m]); // f[m]
return 0;
}
多重リュックサック問題
N種類の品物とV容量のリュックサックがあります.第i種の物品は最大n[i]件が利用可能であり、1件当たりの費用はw[i]、価値はc[i]である.リュックサックにどのアイテムを入れるかを解くと、これらのアイテムの費用の合計がリュックサックの容量を超えず、価値の合計が最大になります.
ここにはもう一つの制限条件があり、各品物が利用可能な回数を規定しています.
同様に,状態遷移方程式を導くことができる:f[i][v]=max(f[i−1][v−kw[i]+kc[i]|0<=k<=n[i])
【問題の説明】
クラスが学校の運動会で全校1位の成績を収めたことを祝うため、担任は祝賀会を開くことを決め、そのために賞品を買って選手をねぎらうことにした.最大価値の賞品を購入できる金額を期待し、精力と体力を補うことができます.
【入力形式】
1行目の2つの数n(n<=500)、m(m<=6000)で、nは購入したい賞品の数を表し、mは送金金額を表す.次にn行は、各行3個分、v,w,sは、それぞれI番目の賞品の価格、価値(価格と価値が異なる概念)と購入数(0個からs個まで可能)を表し、そのうちv<=100、w<=1000、s<=10である.
【出力形式】
1行目:1つの数で、今回の購入で得られる最大の価値を表します(注意!価格ではありません).
【入力サンプル】
5 1000
80 20 4
40 50 9
30 50 7
40 30 6
20 20 1
【出力サンプル】
1040
#include
#include
using namespace std;
int v[6002], w[6002], s[6002];
int f[6002];
int n, m;
int main()
{
scanf("%d%d",&n,&m);
for (int i = 1; i <= n; i++)
scanf("%d%d%d",&v[i],&w[i],&s[i]);
for (int i = 1; i <= n; i++)
for (int j = m; j >= 0; j--)
for (int k = 0; k <= s[i]; k++)
{
if (j-k*v[i]<0) break;
f[j] = max(f[j],f[j-k*v[i]]+k*w[i]);
}
printf("%d
",f[m]);
return 0;
}
dp圧縮後
#include
#include
using namespace std;
int v[10001],w[10001];
int f[6001];
int n,m,n1;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int x,y,s,t=1;
scanf("%d%d%d",&x,&y,&s);
while (s>=t)
{
v[++n1]=x*t;
w[n1]=y*t;
s-=t;
t*=2;
}
v[++n1]=x*s;
w[n1]=y*s; // s 2 :1,2,4,…,2^(k-1),s-2^k+1,
}
for(int i=1;i<=n1;i++)
for(int j=m;j>=v[i];j--)
f[j]=max(f[j],f[j-v[i]]+w[i]);
printf("%d
",f[m]);
return 0;
}
TIP:ちょうどいっぱいなら
このブログの説明を復習してください.http://www.cnblogs.com/fengziwei/p/7750849.html