#include<iostream>
using namespace std;
#define NUM 50//物品数量上限
#define CAP 1500//背包容量上限
int w[NUM];//物品的重量
int v[NUM];//物品的价值
int x[NUM];//记录哪个物品被选择
int p[NUM][CAP];//用于递归的数组 p[i][j]的意思是,当背包容量暂时限定为j时,物品从n,n-1....i挑选。
void knapsack(int n,int c)//n是物品的数量,c是背包的容量
{
//第0行不用,第0列使用,最后答案在p[1][c]。右上角
int jMax = min(w[n] - 1, c);//c一般比较大,这里可以理解为jmax=w[n]-1;
for (int j = 0; j<= jMax; j++)//j<w[n];第n个物品的重量为w[n],当背包容量比w[n]小时,放不开。
{
p[n][j] = 0;//在这个范围的j,p[n][j]=0;
}
for (int j = w[n]; j <= c; j++)//j>=w[n],j从恰巧能放开开始,到最大容量c.
{
p[n][j] = v[n];//此时只放第n个物品,所以p[n][j]=v[n];
}
for (int i = n - 1; i > 0; i--)//从倒数第二行到第一行。
{
jMax = min(w[i] - 1, c);
for (int j = 0; j <= jMax; j++)
{
p[i][j] = p[i+1][j];//j<w[i],在这个范围第i个物品放不开,相当于从第i+1开始。
}
for (int j = w[i]; j <= c; j++)//在能装下的情况下,可装可不装,不装同上,
//装了就相当于从i+1开始往后找,背包容量修改为装了剩余之后的容量,最后加上装入的价值。
{
p[i][j] = max((p[i + 1][j]),(p[i + 1][j - w[i]] + v[i]));//理应从以上两种情况取较大的。
}
}
}
void traceback(int n,int c)//用x数组记录哪件物品被选中,选中的值为1.
{
for (int i = 1; i < n; i++)//第一行到倒数第二行。
{
if (p[i][c] == p[i + 1][c])//如果p[i][c]==p[i+1][c] ,说明有没有第i件物品都一样
{
x[i] = 0;//如果p[i][j]==p[i+1][j],说明不用选。
}
else
{
x[i] = 1;
c = c - w[i];//被选上,背包容量变小。
}
}
if (p[n][c] == 0)//如果最后一行的数等于0 就是没选。反之就是选了。
{
x[n] = 0;
}
else
{
x[n] = 1;
}
cout << "选中的背包号是" << endl;
for (int i = 1; i <= n; i++)
{
if (x[i] == 1)
{
cout << i << " ";
}
}
}
int main()
{
int n,c;
cout << "请输入物品的数量和背包的容量" << endl;
cin >> n>>c;
int x, y;
for (int i = 1; i <= n; i++)
{
cout << "请输入第"<<i << "个物品的重量和价值" << endl;
cin >> x >> y;
w[i] = x;
v[i] = y;
}
knapsack(n,c);
cout << "背包的最大容量为" << p[1][c] << endl;
traceback(n,c);
}