今天也是回忆一下前一段时间,解决的一道对我而言的一道难题,因为本人还是一个小白对算法和数据结构不太了解,所以做起这道题来属实是困难,而这道题我连用笨办法都不会也是直接就学的人家的用动态规划的思路去解决这道问题,这道问题其实也跟01背包问题是一模一样,只需要稍微转化,比如01背包问题里说的大概内容是如何在使背包里的物品不超过一定重量的情况下使得背包内要装的物品价值最高。而这道题里面只需要改变一下思路就i可以和01背包问题变得一样,那就是我们要想如何在不超过物品总价减去包邮的价格的情况下,使得我们要带走的书籍价格最高,那这就相当于把这个个问题反转了过来,最后只需要再一次用总价减去这个最高价,之后就是我们要求的答案了,那显而易见,现在就用到了动态规划的思路:
1.定义原问题和子问题:
原问题是如何在不超过价格M=总价格-包邮价的情况下带走价值最高的书籍组合(这里就需要遍历所有书籍,假设书籍数目为I),那我们就可以把它拆分成小问题,即在不超过m(0<=m<=M)的情况下如何让前i本书组合的价格最高。
2.定义状态:
那么我们根据01背包问题很快就能想到需要的状态量有三个,一个是上限价格,一个是前i本书,一个是相应组合的价格。而其中的组合价格是由前两个决定的
3.寻找状态转移方程:
这里其实就是套用了固有的模板了以上面的题中的示例为例:
这个示例里价格上限就是130,总共有4本书,我们可以使用一个表格来表示一下,在这个表格里面最上一行是各种子问题的上限价格,第一列是我们的前i本书籍,需要填入的就是最佳的价格组合。
... | 20 | .. | 60 | .. | 80 | .. | 90 | .. | 110 | .. | 120 | .. | 130 | |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1(20) | 0 | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 20 |
2(90) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 90 | 90 | 110 | 110 | 110 | 110 | 110 |
3(60) | 0 | 0 | 0 | 60 | 60 | 80 | 80 | 90 | 90 | 110 | 110 | 110 | 110 | 110 |
我们就可以得出这么一个表格,可以看到当我们用书籍总价格减去右下角那个值的时候就是我们需要的答案,这显然在我们也需要在程序里创建类似的表格来这么储存这些数值,而这些数值是怎么计算的呢根据01背包问题也不难得出这个方程就是发f(j-p(i))+p(i)这个就是我们要填入表格数据的依据方式其中j是子问题中的价格上限,p(i)是第i本书的价格,我们需要这个得出的数和p(i-1)来比较大小来决定当前这个表格填入谁,那么假如当前这本书的p(i)>j的话直接填入p(i-1)就可以,f(j-p(i))+p(i)至于这个方程我也是想了很久才想明白其中的意思,这里的f(j-p(i))其实就是J=j-p(i)时的最佳情况,相当于转化为一个新的背包问题,然后加上后面的p(i)就是这本书放进去的最佳组合。
那我们就可以实现程序了我的代码如下:
#include <stdio.h>
#define MAX_P 30001
int main()
{
int n, x;
int arr[MAX_P] = { 0 };
scanf("%d%d",&n,&x);
int i = 0;
int count = 0;
int b[31];//输入书的价格
for (i = 1; i <= n; i++)
{
scanf("%d", &b[i]);
count += b[i];
}
int num = (count - x);//这里用总价格减去包邮条件后正好可以转化为01背包问题
if (num > 30001)//这里分情况讨论是因为我使用的编译器好像用不了太大的数组,要不然会栈溢出,索性就过大的数据省略个位数
{
num /= 10;
for (i = 1; i <= n; i++)
{
int j = 0;
for (j = num; j >= 0; j--)
{
if (b[i] / 10 > j)
arr[j] = 0;
else
{
arr[j] = arr[j - b[i] / 10] + b[i] / 10 > arr[j] ? arr[j - b[i] / 10] + b[i] / 10 : arr[j];//这里用到了滚动数组和动态规划的思想
}
}
}
printf("%d", count - arr[num] * 10);
}
else if (num <= 30000)//另一个判断,内容一样
{
for (i = 1; i <= n; i++)
{
int j = 0;
for (j = num; j >= 0; j--)
{
if (b[i] <= j)
// arr[j] = arr[j];
//else
{
arr[j] = arr[j - b[i]] + b[i] > arr[j] ? arr[j - b[i] ] + b[i] : arr[j];
}
}
}
printf("%d", count - arr[num]);
}
return 0;
}
在这里我们可以将上面的需要创建的二维数组转化为一维数组让这个数组的数据始终更新起来,滚动数组的思想,但是这样就需要从表格的最后一位开始更新,因为我们后面数据的得出是需要上一行前面数据的。这就是我耗费了七天学到的东西,望指正嘿嘿。。