01揹包問題的解決
測試案例:http://poj.org/problem?id=3624
需求:0-1揹包問題(下面程式碼在poj上不是超時就是記憶體消耗過大,畢竟演算法的時間複雜度是O(nW))
xj =0 或1的揹包問題
輸入格式:
行數 內容
1: N M (種類,最大重量)
2~1+N: W_i D_i (單一重量,價值)
狀態轉移方程:f(i,j) = max(f(i,j),f(i-1,j-ui)+wi)
例如:
輸入:
4 6
1 4
2 6
3 12
2 7
輸出:
23
1、二維陣列動態規劃
1.1、Runtime error的版本
狀態轉移方程:f(i,j) = max(f(i,j),f(i-1,j-ui)+wi)
#include<iostream>
#include<math.h>
#define MAX_N 3402 // 最大種類數
#define MAX_W 400 //最大單一種類重量
#define MAX_D 100 // 最大價值
#define MAX_M 12880 //最大限重
using namespace std;
//dp[k][y] 表示在前k中物品中選擇,揹包重量不超過y時揹包最大價值
int dp[MAX_N+1][MAX_M+1];
int weights[MAX_N+1] ;
int values[MAX_N+1] ;
int main() {
int n,m,i,j;
cin >> n >> m;
for (i = 1; i <= n; i++) {
cin >> weights[i] >> values[i];
}
// (1)初始化
for (i = 0; i <= m; i++) {
dp[0][i] = 0; /*不買東西,裝入重量再多的非有價值的情況*/
}
for (i = 0; i <= n; i++) {
dp[i][0] = 0; /*多種東西無價值*/
}
// (2)動態規劃
for (i = 1; i <= n; i++) { //對前i個物品
for (j = 1; j <= m; j++) {//對前j個重量
// 在 同個重量級的前i-1個種類組合 和 在(j-weights[i])的情況下利用前k種物品轉入獲取最大值再加當前物品價值 中選擇
if (j < weights[i]) // 這時候揹包容量不足以放下第 i 件物品,只能選擇不拿,繼續考慮前i-1件物品
dp[i][j] = dp[i - 1][j];
else //這時揹包容量可以放下第 i 件物品,我們可以考慮拿或者不拿,分析怎樣價值更大:
dp[i][j] = max(dp[i - 1][j], dp[i-1][j- weights[i]] + values[i]);
}
}
cout << dp[n][m] << endl;
}
雖然這個演算法在公式上沒有錯誤,但是使用的多行是多餘的,只需要一行儲存就可以,不過,這個版本是《演算法設計與分析》課本教的,可能是為了好理解吧
1.2 展示路徑
#include<iostream>
#include<math.h>
#define MAX_N 3402 // 最大種類數
#define MAX_W 400 //最大單一種類重量
#define MAX_D 100 // 最大價值
#define MAX_M 12880 //最大限重
using namespace std;
//dp[k][y] 表示在前k中物品中選擇,揹包重量不超過y時揹包最大價值
// 不裝第k個物品或者至少裝一個第k種物品
int dp[MAX_N+1][MAX_M+1];
int I[MAX_N + 1][MAX_M + 1]; //標誌陣列,方便回溯,計算優化函式值dp[k][y]時用到物品的最大編號
int weights[MAX_N+1] ;
int values[MAX_N+1] ;
void TrackSolution(int n,int m) {
int x[MAX_N+1] = { 0 }; //n中商品裝入量
int y = m, j = n; // I[J][y]
while (I[j][y] != 0) {
j = I[j][y];
while (I[j][y] == j) {
y = y - weights[j];
x[j]++;
}
}
for (int i = 1; i <= n; i++) {
cout << x[i] << " ";
}
cout << endl;
}
int main() {
int n,m,i,j;
cin >> n >> m;
for (i = 1; i <= n; i++) {
cin >> weights[i] >> values[i];
}
// (1)初始化
for (i = 0; i <= m; i++) {
dp[0][i] = 0; /*不買東西,裝入重量再多的非有價值的情況*/
}
for (i = 0; i <= n; i++) {
dp[i][0] = 0; /*多種東西無價值*/
}
// (2)動態規劃
for (i = 1; i <= n; i++) { //對前i個物品
for (j = 1; j <= m; j++) {//對前j個重量
// 在 同個重量級的前i-1個種類組合 和 在(j-weights[i])的情況下利用前k種物品轉入獲取最大值再加當前物品價值 中選擇
if (j < weights[i])
dp[i][j] = dp[i - 1][j];
else
dp[i][j] = max(dp[i - 1][j], dp[i-1][j- weights[i]] + values[i]);
if (j < weights[i] || (dp[i - 1][j] > dp[i][j - weights[i]] + values[i]))
I[i][j] = I[i - 1][j];
else
I[i][j] = i;
}
}
cout << dp[n][m] << endl;
TrackSolution(n, m);
}
2、回溯法
2.1 、Time Limit Exceeded
#include<stdio.h>
#define MAX_M 12880 //最大限重
#define MAX_N 3402 // 最大種類數
int c;//揹包容量
int n;//物品種類數量
int weight[MAX_N+1]; //存放n個物品種類的陣列
int price[MAX_N+1]; //存放價值的陣列
int currentWeight = 0; //當前重量
int currentPrice = 0; //當前價值
int bestPrice = 0; //當前最優值
int bp = 0; //全域性最優價值
void Backtracking(int i) {
if (i > n) {
if (bestPrice > bp) {
bp = bestPrice;
}
return;
}
if (currentWeight + weight[i] <= c) {
// 把物品i放入揹包,搜尋左子樹
currentWeight += weight[i];
bestPrice += price[i];
Backtracking(i + 1);
currentWeight -= weight[i];
bestPrice -= price[i];
}
Backtracking(i + 1);
}
int main() {
int i;
scanf_s("%d%d", &n,&c);
for (i = 1; i <= n; i++) {
scanf_s("%d%d", &weight[i], &price[i]);
}
Backtracking(1);
printf("%d\n", bp);
}
2.2 展示路徑
#include<stdio.h>
#define MAX_M 12880 //最大限重
#define MAX_N 3402 // 最大種類數
int c;//揹包容量
int n;//物品種類數量
int weight[MAX_N+1]; //存放n個物品種類的陣列
int price[MAX_N+1]; //存放價值的陣列
int currentWeight = 0; //當前重量
int currentPrice = 0; //當前價值
int bestPrice = 0; //當前最優值
int bestAnswer[MAX_M]; //當前最優解
int bp = 0; //全域性最優價值
int bA[MAX_M+1]; //全域性最優路徑
void Backtracking(int i) {
if (i > n) {
if (bestPrice > bp) {
bp = bestPrice;
for (int j = 1; j <= n; j++) {
bA[j] = bestAnswer[j];
}
}
return;
}
if (currentWeight + weight[i] <= c) {
// 把物品i放入揹包,搜尋左子樹
bestAnswer[i] = 1;
currentWeight += weight[i];
bestPrice += price[i];
Backtracking(i + 1);
currentWeight -= weight[i];
bestPrice -= price[i];
}
bestAnswer[i] = 0;
Backtracking(i + 1);
}
int main() {
int i;
scanf("%d%d", &n,&c);
for (i = 1; i <= n; i++) {
scanf("%d%d", &weight[i], &price[i]);
}
Backtracking(1);
printf("%d\n", bp);
for (i = 1; i <= n; i++)
printf("%d ", bA[i]);
}
3、一維陣列動態規劃 AC
注意,for (int j = m; j >= weights[i]; j--)
是逆序遍歷總重量
// 一維陣列解法
#include<iostream>
#define MAX_M 12880 //最大限重
#define MAX_N 3402 // 最大種類數
using namespace std;
int dp[MAX_M + 1];
int weights[MAX_N + 1];
int values[MAX_N + 1];
int main() {
int n, m, i, j;
cin >> n >> m;
for (i = 1; i <= n; i++) {
cin >> weights[i] >> values[i];
}
for (int i = 1; i <= n; i++) { // 種類
for (int j = m; j >= weights[i]; j--) { //容量
dp[j] = max(dp[j], dp[j - weights[i]] + values[i]);
}
}
cout << dp[m] << endl;
}
相關文章
- 揹包問題(01揹包與完全揹包)
- 01揹包問題
- 01揹包、完全揹包、多重揹包詳解
- 從【零錢兌換】問題看01揹包和完全揹包問題
- JavaScript 揹包問題詳解JavaScript
- 揹包問題
- 揹包問題解題方法總結
- 動態規劃 01揹包問題動態規劃
- 【動態規劃】01揹包問題動態規劃
- 動態規劃-01揹包問題動態規劃
- 動態規劃--01揹包問題動態規劃
- ACM 揹包問題ACM
- 窮舉法解決0/1揹包問題——pythonPython
- 【動態規劃】01揹包問題【續】動態規劃
- 揹包問題大合集
- 詳解動態規劃01揹包問題--JavaScript實現動態規劃JavaScript
- 詳解動態規劃01揹包問題–JavaScript實現動態規劃JavaScript
- 01揹包、有依賴的揹包
- 動態規劃之01揹包問題(最易理解的講解)動態規劃
- 揹包問題的演算法演算法
- leetcode題解(0-1揹包問題)LeetCode
- JavaScript中揹包問題(面試題)JavaScript面試題
- 揹包問題例題總結
- 01揹包面試題系列(一)面試題
- 揹包問題的一道經典問題
- poj1276 多重揹包問題(二進位制解決方案)
- 01 揹包
- 動態規劃系列之六01揹包問題動態規劃
- 雙核處理(動態規劃的01揹包問題)動態規劃
- 動態規劃解0-1揹包問題動態規劃
- 揹包問題----動態規劃動態規劃
- 【動態規劃】揹包問題動態規劃
- Java實現-揹包問題IJava
- Java實現-揹包問題IIJava
- Java實現-揹包問題VIJava
- chapter12-2-揹包問題APT
- 二維費用揹包問題
- 01揹包問題理解動態規劃演算法動態規劃演算法