先看例题再上代码:
i(物品编号) | 1 | 2 | 3 | 4 |
w(i)(体积) | 2 | 3 | 4 | 5 |
v(i)(价值) | 3 | 4 | 5 | 6 |
求背包装入物品的最大价值总和:V,
#include<stdio.h>
int v[5]={0,3,4,5,6};
int w[5]={0,2,3,4,5};
int flag[5];
int dp[5][9];
int bagV=8;
int max(int a,int b)
{
int t=(a>b)?a:b;
return t;
}
void findmax()
{
for(int i=1;i<5;i++)
for(int j=1;j<=bagV;j++)
{
if(j<w[i])
{
dp[i][j]=dp[i-1][j];
}
else
{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
}
}
}
void findwhat(int i,int j)
{
if(i>=0)
{
if(dp[i][j]==dp[i-1][j])//感觉不太严谨的亚子;
{
findwhat(i-1,j);
}
else if(j>=w[i]&&dp[i][j]==dp[i-1][j-w[i]]+v[i])
{
flag[i]=1;
findwhat(i-1,j-w[i]);
}
}
}
void print()
{
for(int i=0;i<5;i++)
{
if(flag[i]==1)printf("%d ",i);
}
}
int main()
{
findmax();
findwhat(4,8);
print();
return 0;
}
对于这个我学了一个小时,打了一个小时的代码,今天它能被我放在这里我表示无比的欣慰:然后来浅浅的解释一下代码:首先定义若干个有关全局变量,flag数组用来标记,dp来打表,bagV是书包容量,先定义一个求较大值的函数,然后再用一个函数来找最大价值,:用花色来解释一下这个函数:画图吧:
*
现在来讲一下01背包的基本理念:嗯,注意前方高能:
作为动态规划的一员,我们要考虑方方面面都要为自己谋求最大价值,那排是在背包里面放东西也不例外,我们尽量在有限的时间里面放最有价值的东西,这也就是为什么放个东西还要关注这么多,也就是因为每个东西的所占空间和价值不一样,才会有如此限制让我们用贪心不能拿到最大,因为尽管你拿到的是最大价值的一个东西且有空间装得下,但是可能会有其他单个价值不是最大但是容量小,和别的东西放进去占的空间和刚刚的物品一样大的空间但价值就是更大一些,所以我们要让每一步走的都是最大价值,而之所以要给定每一个容量,也是因为这个原因,然而那个代码里面如果给定的容量装不下此时序号东西的时候要返回上一个东西所得的最大价值,这里可以理解为,上一个东西是没有拿此时序号的东西从而得到的价值最大值,而这里序号的我的容量不够,不和上面的一样吗?而最难理解的就是能拿,我们可以认为,如果可以有空间拿到的话,那就要先减去这个物品的容量来装它,并在减去后的剩余容量里面找到它的最大价值也就是如图黄色圈,然后再加上它的价值,此时的总价值和不装它的情况比较,最终的就是此格的最大价值。
这样就找到了所有情况的最大值,但是这时候我们要找到最终到底装了哪些呢?这时候我们就要回溯背包,也就是第二个函数,我们来对它画图分析一下
关于橙色的图里文字不小心少打了一些,在这里添上 ( 可能有的人会觉得这个题目的价值和容量是按顺序的,那我们是不是也要对它进行排序呢?其气质这纯属 )而随后一个函数就是打印出来拿了的元素,琢磨一下就出来了,没什么稀奇。
然后我们就可以总结一下01背包的题目特点以及做题步骤:
对于每个东西我们都有两种选择,且一般是求最值;
步骤就是:先建立模型,及求所求的关系式,然后再找一个条件限制,最后在寻找到前后的递推关系式。
---------------------------------------------------------------------------------------------------------------------------------
而今天的重点就应该是背包的应用先看一个题:
题目背景
kkksc03 的大学生活非常的颓废,平时根本不学习。但是,临近期末考试,他必须要开始抱佛脚,以求不挂科。
题目描述
这次期末考试,kkksc03 需要考 44 科。因此要开始刷习题集,每科都有一个习题集,分别有 s1,s2,s3,s4 道题目,完成每道题目需要一些时间,可能不等(A1,A2,…,As1,B1,B2,…,Bs2,C1,C2,…,Cs3,D1,D2,…,Ds4)。
kkksc03 有一个能力,他的左右两个大脑可以同时计算 22 道不同的题目,但是仅限于同一科。因此,kkksc03 必须一科一科的复习。
由于 kkksc03 还急着去处理洛谷的 bug,因此他希望尽快把事情做完,所以他希望知道能够完成复习的最短时间。
输入格式
本题包含 5 行数据:第 1 行,为四个正整数 s1,s2,s3,s4。
第 2 行,为 A1,A2,…,As1 共 s1 个数,表示第一科习题集每道题目所消耗的时间。
第 3 行,为B1,B2,…,Bs2 共 s2 个数。
第 4 行,为 C1,C2,…,Cs3 共 s3 个数。
第 5 行,为D1,D2,…,Ds4 共 s4 个数,意思均同上。
输出格式
输出一行,为复习完毕最短时间。
输入输出样例
输入
1 2 1 3
5
4 3
6
2 4 3
输出
20
说明/提示
1≤s1,s2,s3,s4≤20。
1≤A1,A2,…,As1,B1,B2,…,Bs2,C1,C2,…,Cs3,D1,D2,…,Ds4≤60。
看到这个题可能可能会想到贪心(偏了一点),想到01背包但是就是不知道 怎么用,刚开始的时候我是觉得打不着半毛钱关系,完全没思路,看了其他人各式各样的思路我才把它写出来,先看看代码:
#include<stdio.h>
int max(int a,int b)
{
int t=(a>b)?a:b;
return t;
}
int a[400];
int s[10000];
int b[10000];
int t;
int main()
{
for( int i=0; i<4; i++)
scanf("%d",&a[i]);
for(int i=0; i<4; i++)
{ int sum=0;//记得每场都要初始化
for(int j=0; j<a[i]; j++)
{
scanf("%d",&s[j]);
sum+=s[j];
}
for(int j=0;j<a[i];j++)
{
for(int k=sum/2;k>=s[j];k--)
{
b[k]=max(b[k],b[k-s[j]]+s[j]);
}
}t+=sum-b[sum/2];
for(int j=0;j<=sum/2;j++)
b[j]=0;//清零要记得的
}
printf("%d\n",t);
return 0;
}
刚开始我的代码一直过不了,后来乱改一通,才知道是数组开小了,不符合条件;
这里由于时间原因就只先说一下思路,大概就是,我们自己去看这道题的话首先想到的肯定是先让左右脑的时间相差最小然后去其中较大的时间作为该科目的最终用时。所以这里就有了一个东西叫t/2,你会发现当你这么想的时候,你的这个时间一定是无比接近t/2最好是它,所以,我们可以这么写。(由于时间原因,具体的以后再改新的代码)这个代码我出现的几个问题是没有在一场大循环的时候把sum初始化,而为什么要初始化呢,这是由于我们求的和都只是单个科目而非4个,所以每次都要进行一次归零,下面s数组的归零也是一样的的,具体的有时间再详细描述一下代码,这代码我学了好久,最近有点赶。
但是什么都不能少了文章的灵魂:“若心有所向,平凡的日子也会泛着光,光终究会撒在你的身上 你也会灿烂一场。”