DP学习_0-1背包问题
问题描述:
给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包容量为c。问应如何选择装入背包中的物品,使得装入背包中物品的总价值最大。在选择装入背包的物品时,对每种物品i只有两种选择,即装入背包或不装入背包。不能将物品i装入背包多次,也不能只装入部分的物品i。因此,该问题称为0-1背包问题。
算法分析:
DP的问题可以转化为一种维护备忘录(表)的思想,0-1背包问题是DP的基本问题,其本质在于自底向上地维护下面所示的一张表:
案例假设:n=3; c=6;
物品的重量w与价值v如下:
物品 |
0 |
1 |
2 |
w |
3 |
2 |
1 |
v |
3 |
2 |
1 |
程序要维护的表(人工算出):
i j |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
2 |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
0 |
1 |
2 |
3 |
3 |
3 |
3 |
0 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
维护过程说明:
首先在初始化时提供i=2这一行的值,程序运行时遵循以下原则:
1. 当i=n-1时,若j>=w[i],则m(i,j)=v[i];若0<=j<w[i],则m(i,j)=0
2. 当i<n-1时,若j>=w[i],则m(i,j)=max{ m(i+1,j), m(i+1,j-w[i])+v[i] };若0<=j<w[i],则m(i,j)=m(i+1,j)
对于这个规则的分析:
【m(i+1,j) 容量为j,不新添加第i个物品时的最大价值】
【m(i+1,j-wi)+v[i] 容量j去掉第i个物品质量时的最大值(表中在之前已经求出)直接加上v[i],即强制添加第i个物品】
【需要添加i的情况的剪贴法解释:强制添加第i物品比不添加,总价值更高】
根据以上分析产生的代码:
//0-1 Knapsack problem #include <iostream> #include <vector> using std::cout; using std::vector; using std::cin; using std::endl; #define MAX_KNAPSACK 10 #define MAX_CONTAIN 50 class KPC { private: int v[MAX_KNAPSACK];//Value int w[MAX_KNAPSACK];//Weight int f[MAX_KNAPSACK][MAX_CONTAIN];//Best Value vector<int> item[MAX_KNAPSACK][MAX_CONTAIN];//Item List int c;//Contain int n;//Item Size public: void DP();//物品起始号,当前容量 void PrintVector(vector<int> vect);//打印某一格的物品清单 }; void KPC::PrintVector(vector<int> vect) { if(vect.begin() == vect.end()) return; vector<int>::iterator it; for(it=vect.begin(); it!=vect.end()-1; it++) { cout << *it; cout << "."; } cout << *it; } void KPC::DP() { cin >> n >> c;//先输入物品数,然后是容量 for(int i=0; i!=n; i++) { cin >> w[i] >> v[i];//先重量,后价值 } //初始化的值将作为基点 //空的部分 int min = w[n-1] < c ? w[n-1] : c; int j=0; for(; j<min; j++) { f[n-1][j] = 0; } //有物部分的第一行 for(; j<=c; j++) { f[n-1][j] = v[n-1]; item[n-1][j].push_back(n-1); } for(int j = 0; j <= c; j++)//按上到下、左到右顺序填表 { for(int i=n-2; i>=0; i--)//总质量一定,找到最优值 { if(j < w[i]) { for(int t=j; t<=w[i]; t++) { f[i][t] = f[i+1][j]; item[i][j] = item[i+1][j];//继承 } } else//j == w[i] { if(f[i+1][j] > f[i+1][j-w[i]] + v[i]) { f[i][j] = f[i+1][j]; item[i][j] = item[i+1][j];//继承 } else { f[i][j] = f[i+1][j-w[i]] + v[i]; item[i][j] = item[i+1][j-w[i]]; item[i][j].push_back(i);//添加新物品 } } } } //输出右下角的值作为最大价值的解 cout << f[0][c] << endl; //物品的解 PrintVector(item[0][c]); cout << endl; //打印填好的价值表 for(int i=n-1; i>=0; i--) { for(int j=0; j<=c; j++) { cout << f[i][j] << " "; } cout << endl; } cout << "-------------------" << endl; //打印填好的物品表 for(int i=n-1; i>=0; i--) { for(int j=0; j<=c; j++) { cout << "["; PrintVector(item[i][j]); cout << "]"; } cout << endl; } } int main() { KPC KP; KP.DP(); system("pause"); }
运行结果:
3 6
3 3
2 2
1 1
6
2.1.0
0 1 1 1 1 1 1
0 1 2 3 3 3 3
0 1 2 3 4 5 6
——————-
[][2][2][2][2][2][2]
[][2][1][2.1][2.1][2.1][2.1]
[][2][1][0][2.0][1.0][2.1.0]
Press any key to continue . . .
5 10
1 10
3 5
2 6
9 8
4 7
28
4.2.1.0
0 0 0 0 7 7 7 7 7 7 7
0 0 0 0 7 7 7 7 7 8 8
0 0 6 6 7 7 13 13 13 13 13
0 0 6 6 7 11 13 13 13 18 18
0 10 10 16 16 17 21 23 23 23 28
——————-
[][][][][4][4][4][4][4][4][4]
[][][][][4][4][4][4][4][3][3]
[][][2][2][4][4][4.2][4.2][4.2][4.2][4.2]
[][][2][2][4][2.1][4.2][4.2][4.2][4.2.1][4.2.1]
[][0][0][2.0][2.0][4.0][2.1.0][4.2.0][4.2.0][4.2.0][4.2.1.0]
Press any key to continue . . .