複数のナップザックの問題(暴力、バイナリ最適化、単調キュー最適化)

ヤンシェンビデオノート

ビデオリンク

マルチバックパックと01バックパックの違いは、マルチバックパック内のアイテムの数に指定された数量制限があることです。

複数のナップザック問題I(トピックリンク

問題解決のアイデア:

この質問のデータ範囲は比較的小さいため、最も暴力的な方法を直接使用できます。つまり01バックパックに基づいて、アイテムの数をトラバースできます。

コード:

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<cstdio>
#include<cmath>
using namespace std;
const int N = 110;
int f[N]; 
int main(){
    
    
//	freopen("1.txt","r",stdin);
	int n,m,v,w,s;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
    
    
		cin>>v>>w>>s;
		for(int j=m;j>=v;j--){
    
    
			for(int k=0;k<=s&&k*v<=j;k++){
    
    
				f[j] = max(f[j],f[j-v*k]+w*k);
			}
		}
	}
	cout<<f[m]<<endl; 
	return 0;
}

複数のナップザック問題II(トピックリンク

問題解決のアイデア:

データ範囲となる0 <N≤1000、0 <V≤2000、0 <V iは、wはI、S、iは≤2000、元の暴力的方法を使用することができません。だからあなたは使う必要がありますバイナリ最適化(未使用)、いわゆるバイナリ最適化は、複数のナップザック問題を01ナップザック問題に変換するために、数値を可能な限り少ない数値(1,2,4,8 ...)に分割することです。バイナリ最適化のプロセスでは、分割された数値の合計が元の数値と等しいことを確認する必要があります。そうでない場合、存在しない結果が生成されます。つまり、最後の数字は元の番号マイナス分割された数値の合計結果として。分離の結果は01ナップザックの問題になります。01ナップザックメソッドを使用して対処してください。

コード:

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<cstdio>
#include<cmath>
using namespace std;
const int N = 2010;
struct Good{
    
    
	int v,w;
}; 
vector<Good>goods;
int f[N],n,m,v,w,s;
int main(){
    
    
//	freopen("1.txt","r",stdin);
	cin>>n>>m;
	for(int i=0;i<n;i++){
    
    
		cin>>v>>w>>s;
		for(int k=1;k<=s;k*=2){
    
    
			s-=k;
			goods.push_back({
    
    v*k,w*k});
		}
		if(s>0) goods.push_back({
    
    v*s,w*s});
	}
	for(auto good:goods){
    
    
		for(int i=m;i>=good.v;i--){
    
    
			f[i] = max(f[i],f[i-good.v]+good.w);
		}
	}
	cout<<f[m]<<endl;
	return 0;
}
Dev C ++は自動で実行できませんか?解決策:Dev C ++にC ++ 11をサポートさせます

複数のナップザック問題III(トピックリンク

問題解決のアイデア:

この質問では、単調キューの最適化を使用する必要があります。最初に単調キューを確認することをお勧めします(難しくはありません)。複数のナップザック問題のコアコードを観察することによって私は

#include<iostream>
using namespace std;
const int N = 110;
int f[N]; 
int main(){
    
    
//	freopen("1.txt","r",stdin);
	int n,m,v,w,s;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
    
    
		cin>>v>>w>>s;
		//*******************核心代码*******************
		for(int j=m;j>=v;j--){
    
    //循环一
			for(int k=0;k<=s&&k*v<=j;k++){
    
    //循环二
				f[j] = max(f[j],f[j-v*k]+w*k);
			}
		}
		//*********************************************
	}
	cout<<f[m]<<endl; 
	return 0;
}

いくつかの冗長な二重計算があります。たとえば、m = 100、v = 2、k = 3の
場合、j = 100の場合、f [100]、f [98]、f [96]、f [94]を使用し、最大値
をj =として選択する必要があります。98の場合は、f [98]、f [96]、f [94]、f [92]
を使用する必要があります。その中から最大値を選択してください。j= 96の場合は、f [96]、f [94]、を使用する必要があります。f [92]、f [90]、それらの中から最大値を選択する
、その考え方は単調なキュー(スライディングウィンドウ)とまったく同じであることがわかりますしたがって、mからv(m%v)の係数を分類して議論することができます。残りの部分ごとに、単調なキューのアイデアを使用して最大値を見つけます(比較値のサイズはサイズに直接比例しません。コード分析を参照してください)。次の分析は、コードに基づいています。

コード:

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<cstdio>
#include<cmath>
using namespace std;
const int N = 20010;
int n,m;
int f[N],g[N],q[N]; 
int main(){
    
    
//	freopen("1.txt","r",stdin);
	cin>>n>>m;
	for(int i=0;i<n;i++){
    
    
		int v,w,s;
		cin>>v>>w>>s;
		//g数组保存的是上一轮的结果(即前i-1个物品在体积为0~m的最优解)
		memcpy(g,f,sizeof f);
		//分别讨论每个余数 
		for(int j=0;j<v;j++){
    
    
			int hh=0,tt=-1;
			for(int k=j;k<=m;k+=v){
    
    
				f[k] = g[k];
				//去除超出范围的数(即当前的体积-第i个物品最大可能的体积>队首的体积) 
				if(hh<=tt&&k-s*v>q[hh]) hh++;
				//用最大的数去更新当前的最优解
				//可以先看后面的代码,再看这一行,这样比较好理解
				if(hh<=tt) f[k] = max(f[k],g[q[hh]]+(k-q[hh])/v*w);
				//剔除队列里一定不会被用到的数 
				//闫神的代码,没看懂
//				while(hh<=tt&&g[q[tt]]-(q[tt]-j)/v*w<=g[k]-(k-j)/v*w) tt--;
				//我的代码,转换一下就和闫神的一样了,至少我写的代码我看的懂一点
				//观察多重背包问题I的核心代码,就能理解括号中的比较是怎么比大小的了
				//实在看不懂,最后有讲解。
				while(hh<=tt&&g[q[tt]]+(k-q[tt])/v*w<=g[k]) tt--; 
				//把当前数加到队列里去 
				q[++tt] = k;
			}
		}
	}
	cout<<f[m]<<endl;
	return 0;
}

オン比較する説明q [tt]kの2つの値があり、どちらがより「大きい」かを判断する方法は?たとえば、ボリュームx(x%v、q [tt]%v、k%vが等しい)の最適なソリューションが必要な場合、最適なソリューションは次のようになります。g [q [tt]] +(xq [tt])/ v * wg [k] +(xk)/ v * w前者の最大値が大きい場合はq [tt]が「大きい」ことを意味し、そうでない場合はkが「大きい」ことを意味します。単調キューの考え方によれば、q [tt]がkより小さい場合q [tt]を削除する必要があります。つまり、tt–です。
最後の複数のナップザック問題IIIは理解するのが少し難しいです、そしてあなたはそれを数回読んだ後にそれを理解することができます。

おすすめ

転載: blog.csdn.net/lmmmmmmmmmmmmmmm/article/details/107174162