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揹包和完全揹包問題解法模板
- 2. 01揹包問題
- 01揹包、完全揹包、多重揹包詳解
- 從【零錢兌換】問題看01揹包和完全揹包問題
- 你真的懂01揹包問題嗎?01揹包的這幾問你能答出來嗎?
- 動態規劃 01揹包問題動態規劃
- 【動態規劃】01揹包問題動態規劃
- 動態規劃--01揹包問題動態規劃
- 動態規劃-01揹包問題動態規劃
- 【動態規劃】01揹包問題【續】動態規劃
- 詳解動態規劃01揹包問題--JavaScript實現動態規劃JavaScript
- 詳解動態規劃01揹包問題–JavaScript實現動態規劃JavaScript
- 01揹包、有依賴的揹包
- 【模板】01揹包、完全揹包
- 01 揹包
- 揹包問題
- 窮舉法解決0/1揹包問題——pythonPython
- 揹包問題解題方法總結
- 動態規劃系列之六01揹包問題動態規劃
- 01揹包面試題系列(一)面試題
- leetcode題解(0-1揹包問題)LeetCode
- 用各種方法解01揹包
- Day 37 | 01揹包問題 、416. 分割等和子集
- 動態規劃-揹包01問題推理與實踐動態規劃
- 01揹包問題理解動態規劃演算法動態規劃演算法
- 01 揹包的變形
- 強化學習--策略迭代如何解決01揹包問題?內附程式碼強化學習
- 揹包九講問題
- 經典揹包問題
- 揹包問題大合集
- 部分揹包問題(挖
- 005多重揹包問題||
- POJ 2184 (01揹包)
- 程式碼隨想錄演算法訓練營第41天 | 01揹包問題 二維 、 01揹包問題 一維 、 416. 分割等和子集演算法
- 揹包問題例題總結