動態規劃-揹包類

_Gion發表於2017-08-28

(一)01揹包

有個揹包,可以裝重為V的物品。有n種物品,第i個物品的重量(或說是空間)是v[i], 價值是c[i],每種物品只有一件,求能裝的最大價值

二維解法:

定義二維陣列f[i][j],表示前i件 不超過j的物品的最大價值

動態轉移方程:(根據第i件物品取或不取)

f[i][j] = max(f[i-1][j], f[i-1][j-w[i]] + c[i]);

文字的解釋:

i件重不超過j的物品的最大價值=取下面兩項之最優

1 f[i-1][j]表示前i-1件重j的物品的最大價值,也就是 第i件沒取,前i件的最大價值就是前i-1件的最大價值;

2 f[i-1][j-w[i]] + c[i] 表示前i-1件重j-w[i]的物品的最大價值 也就是第i件取了,佔了w[i]的重量,增加了c[i]的價值

解是 f[n][V]  (前n個物品重不超過V的最大價值)

 

#include <iostream>
using namespace std;

int n, V;
int v[200], c[200];
int f[1001][1001];  // f[i][j] --> 前i個物品,重量不超過j的最大價值 

int main() {
    cin >> V >> n;
    for(int i=1; i<=n; i++) cin >> v[i] >> c[i];
    for(int i=1; i<=n; i++) { //迴圈前i件物品
        for(int j=V; j>0; j--) { //迴圈重量j
            if(v[i] <= j) f[i][j] = max(f[i-1][j], f[i-1][j-v[i]] + c[i]);
            else f[i][j] = f[i-1][j];      //放不下 
        }
    }
    cout << f[n][V] << endl;
    return 0;
}



一維解法:

定義陣列f[j], 表示重不超過j的物品的最大價值

動態轉移方程:(根據第i件物品取或不取)

f[j] = max(f[j], f[j-w[i]] + c[i]);

j迴圈必須是j=V...0

#include <iostream>
using namespace std;

int n, V;
int v[200], c[200];
int f[1001];  // f[j] --> 重量不超過j的最大價值 

int main() {
    cin >> V >> n;
    for(int i=1; i<=n; i++) cin >> v[i] >> c[i];
    for(int i=1; i<=n; i++) {        //迴圈前i件物品
        for(int j=V; j>=v[i]; j--) { //迴圈重量j (j<v[i]時放不下,不操作)
            f[j] = max(f[j], f[j-v[i]] + c[i]);   
        }
    }
    cout << f[V] << endl;
    return 0;
}




(二)完全揹包

有個揹包,可以裝重為V的物品。有n種物品,第i個物品的重量是v[i], 價值是c[i],

每種物品有無限件,求能裝的最大價值



一維解法:

定義陣列f[j], 表示重不超過j的物品的最大價值

動態轉移方程:同01揹包一維解法一樣(根據第i件物品取或不取)

f[j] = max(f[j], f[j-w[i]] + c[i]);

01不同的是:

j迴圈必須是j=0...V

重不超過j的物品的最大價值

解是f[V](重量不超過V的最大價值)


#include <iostream>
using namespace std;

int n, V;
int v[200], c[200];
int f[1001];  // f[j] --> 重量不超過j的最大價值

int main() {
	cin >> V >> n;
	for(int i=1; i<=n; i++) cin >> v[i] >> c[i];
	for(int i=1; i<=n; i++) {        //迴圈前i件物品
		for(int j=v[i]; j<=V; j++) { //迴圈重量j (j<v[i]時放不下,不操作)
			f[j] = max(f[j], f[j-v[i]] + c[i]);
		}
	}
	cout << f[V] << endl;
	return 0;
}



(三)多重揹包

有個揹包,可以裝重為V的物品。有n種物品,第i個物品的重量是v[i], 價值是c[i],

每種物品有s[i]件,求能裝的最大價值


一維解法:

定義陣列f[j], 表示重不超過j的物品的最大價值

動態轉移方程:(根據第i件物品取或不取)

i=1...n;

   j=V...0;

      k=0...s[i]     //K迴圈件數s[i]

     f[j] = max(f[j], f[j-w[i]*k] + c[i]*k);

 (多重揹包與01揹包一樣:j迴圈是j=0...V)

解是f[V];



#include <iostream>
using namespace std;

int n, V;
int v[200], c[200], s[200];
int f[1001];  // f[j] --> 前需要佔重量不超過j的最大價值

int main() {
	cin >> V >> n;
	for(int i=1; i<=n; i++) cin >> v[i] >> c[i] >> s[i];
	for(int i=1; i<=n; i++) {
		for(int j=V; j>=0; j--) {
			for(int k=0; k<=s[i]; k++) {
				if(j-v[i]*k < 0) break; //k是遞增的,揹包不夠k迴圈結束
				f[j] = max(f[j], f[j-v[i]*k] + c[i]*k);
			}
		}
	}
	cout << f[V] << endl;
	return 0;
}




(四)混合揹包

有個揹包,可以裝重為V的物品。有n種物品,第i個物品的重量是v[i], 價值是c[i],

有的物品有1件,有的有s[i]件,有的有無限件,求能裝的最大價值


解法:

i=1...n;

if(第i件是完全揹包) ..

else if(第i件是多重揹包) ..

//只能取1件作為s[i]為1的物品

 

可以根據混合情況變通if語句

 

三種揹包-混合揹包:


#include <iostream>
using namespace std;

int n, V;
int v[1001], s[1001], c[1001];
int f[1001];

int main() {
	cin >> V >> n;
	for(int i=1; i<=n; i++) cin >> v[i] >> c[i] >> s[i];
	for(int i=1; i<=n; i++) {
		if(s[i] == 0) {
			for(int j=v[i]; j<=V; j++)
				f[j] = max(f[j], f[j-v[i]]+c[i]);

		} else {
			for(int j=V; j>=v[i]; j--)
				for(int k=1; k<=s[i]; k++) { 
					if(j >= k * v[i]) {
						f[j] = max(f[j], f[j - k * v[i]] + k * c[i]);
					} else break;
				}
		}
	}
	cout << f[V] << endl;
	return 0;
}



(五)二維費用揹包

二維費用只是直接在原有揹包上加一維度和迴圈

 

1原來f[j]表示V不超過j的最大價值

現在f[j][k]表示V1不超過j且V2不超過k的最大價值

 

2 原來f[j] = max(f[j],f[j-v[i]] + c[i]);

現在f[j][k] = max(f[j][k], f[j-v1[i]][k-v2[i]] + c[i]);

 

這樣連三維費用揹包都會了..

 

這裡直接是二維費用的混合三種揹包

 



#include <iostream>
using namespace std;

int n, V1, V2;
int v1[1001], v2[1001], m[1001], c[1001];
int f[1001][1001];

int main() {
    cin >> n >> V1 >> V2;
    for(int i=1; i<=n; i++) cin >> v1[i] >> v2[i] >> m[i] >> c[i];
    for(int i=1; i<=n; i++) {
        if(m[i] == 0) { //如果第i件是完全揹包 
            for(int j=v1[i]; j<=V1; j++) { 
                for(int k=v2[i]; k<=V2; k++) {
                    f[j][k] = max(f[j][k], f[j-v1[i]][k-v2[i]] + c[i]);
                }
            }
        } else { //如果第i件數量有限(01或多重) 
            for(int j=V1; j>=v1[i]; j--) {
                for(int k=V2; k>=v2[i]; k--) {
                    for(int s=1; s<=m[i]; s++) {
                        if(j - s*v1[i] < 0 || k - s*v2[i] < 0) break;
                        f[j][k] = max(f[j][k], f[j-v1[i]*s][k-v2[i]*s] + c[i]*s);
                    }
                }
            }
        }
    }
    cout << f[V1][V2] << endl;
    return 0;
}


(六)有依賴性的揹包

 

【金明的預算方案】

 

【解法】分五種情況處理主件:不取,只取主件,取主件和附件1,取主件和附件2,取主件和兩個附件.找最優情況

 

#include <iostream>
using namespace std;

int V, n, num;     //V是金錢,n是物品總數,num記錄主件數 
int v[61][4];      //v[i][0]記錄有幾個附件了,v[i][1]:主件的價格,v[i][2]:附件1,v[i][3]:附件2 
int p[61][4];      //同上,記錄重要度 
int f[61][32001];  //f[i][j]表示前i件主件價格為j的最大價值 
int map[61];       //i對映到v、p陣列(num) 

int main() {
	int tmpv, tmpp, follow;
	cin >> V >> n;
	for(int i=1; i<=n; i++) {
		cin >> tmpv >> tmpp >> follow;
		if(follow == 0) {
			v[++num][1] = tmpv;
			p[num][1] = tmpp;
			map[i] = num;
			continue;
		}
		follow = map[follow];
		v[follow][++v[follow][0]+1] = tmpv;
		p[follow][++p[follow][0]+1] = tmpp;
	}
	for(int i=1; i<=num; i++) {
		for(int j=1; j<=V; j++) {
			f[i][j] = f[i-1][j];
			if(j >= v[i][1]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]]+v[i][1]*p[i][1]);
			if(j >= v[i][1]+v[i][2]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]-v[i][2]]+v[i][1]*p[i][1]+v[i][2]*p[i][2]);
			if(j >= v[i][1]+v[i][3]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]-v[i][3]]+v[i][1]*p[i][1]+v[i][3]*p[i][3]);
			if(j >= v[i][1]+v[i][2]+v[i][3]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]-v[i][2]-v[i][3]]+v[i][1]*p[i][1]+v[i][2]*p[i][2]+v[i][3]*p[i][3]);
		}
	}
	cout << f[num][V] << endl;
	return 0;
}

 

(七)分組揹包

N件物品,第i個物品的重量是v[i], 價值是c[i],每個物品有自己的組,每組只能選一個物品,求能裝的最大價值

(可以看下原題 洛谷-分組揹包

#include <iostream>
using namespace std;

int v[200][1010], c[200][1010];
int F[1010];
int V, N, G;

int main() {
	int v1, c1, opt;
	cin >> V >> N;
	for(int i=1; i<=N; i++) {
		cin >> v1 >> c1 >> opt;
		G = max(G, opt);
		v[opt][++v[opt][0]] = v1;
		c[opt][++c[opt][0]] = c1;
	}
	for(int i=1; i<=G; i++)
		for(int j=V; j>=0; j--) 
			for(int k=1; k<=v[G][0]; k++)
				if(j >= v[i][k])
					F[j] = max(F[j], F[j-v[i][k]] + c[i][k]);
	cout << F[V] << endl;
	return 0;
}



QWQ,就這樣了...#end

相關文章