【问题描述】
有一个背包,其容量为40,现在有N件物品,1,2,3,4……N,每件物品的体积为a[i](1<=i<=N),如果你可以刚好利用这N件物品将这个背包装满,那么这些东西就是你的,问你一共有多少中选法?
第一行输入一个整数N表示一共有多少个物品,第二行有N个数,每个数代表这个物品的体积。
输出一个整数,代表一共有多少中选法。
【我的思路】
这道题当然首先想到的是用递归,要做的第一步当然是从前k件物品里凑出体积为m的物品,way(k,m),我们最后求出way(N,40)就行,用递归的方法来做,从最右边开始选一个物品,然后剩下的问题就是从第2到m件物品中凑出体积为m-a[1]的物品way(k-1,m-a[k]),但是我们也不一定非要选第一件物品啊,要是不选的话就要从第2到m件物品中凑出体积为m的物品way(k-1,m),边界条件是当k = 0,意思是没有物品了,当然返回0,m = 0,意思是没有空余空间了,说明凑满了,这当然是一种凑法,返回1。上代码:
#include<iostream>
using namespace std;
int bg[1010];
int Way(int k, int m)
{
if (m == 0) return 1;
if (k <= 0) return 0;
return Way(k - 1, m - bg[k]) + Way(k - 1, m);
}
int main()
{
int N;
cin >> N;
for (int i = 1; i <= N; i++)
{
cin >> bg[i];
}
cout << Way(N, 40) << endl;
return 0;
}
上面的代码不严谨,在递归的时候应该判断一下接下来的那件物品的体积会不会直接比整个背包体积还大,这样的话一旦输入的有一个物品的体积直接大于整个背包的体积时会出错。我这里就不改了。
这种方法的时间复杂度当N很大时,也就是可以选择的物品很多的时候程序会跑不出来,时间复杂度为N的平方,更好的是用动态规划来做这道题:
我们用dp[i][j]来保存i个物品凑成体积为j的方法个数,那么最后输出dp[40][N]即可。和 递归一样的道理,我就不过多阐述了,直接上代码:
#include<iostream>
using namespace std;
int bg[1010];
int dp[1010][1010];
int main()
{
int N;
memset(dp,0,sizeof(dp));
cin >> N;
for (int i = 1; i <= N; i++)
{
cin >> bg[i];
dp[0][i] = 1;
}
dp[0][0] = 1;
for (int i = 1; i <= 40; i++)
{
for (int j = 1; j <= N; j++)
{
dp[i][j] = dp[i][j-1];
if (i - bg[j] >= 0)
dp[i][j] += dp[i- bg[j]][j-1];
//不能直接dp[i][j] = dp[i][j-1] + dp[i- bg[j]][j-1];
//因为可能第j件物品的体积直接大于整个背包
}
}
cout << dp[40][N] << endl;
return 0;
}
可以看出来,这个方法的时间复杂度为40*N,不用担心效率的问题。虽然不用担心效率问题但是这道题空间是40,已经明确不是很大,但假如这个数很大呢,N也很大的同时就要开辟一个很大的数组,这样的话在比赛的时候可能就会超内存,所以空间还可以优化。
我们用二维数组dp[i][j]存放的时候,dp[i][j] = dp[i][j-1] + dp[i- bg[j]][j-1]这个递推式好好看看想想,想一下如果我们把j放在外层循环,就是先看有几件物品,然后看背包容量,一件物品要装满i容积的口袋,一直到j件物品要装满那么多的容量,是不是在算下一件物品时用到上一件物品的数据用完了上一件物品的数据就没用了,反正我们最后要输出的是最大的,那为什么不直接就放在上一件物品的数据(相当于更新),以此类推,我们用一个一维数组就可以了,但有一个前提,你在覆盖上一个数据之前吧保证这个物品的下面(也就是容积大的部分)已经算完了,不然可能人家还要用到,所以我们从大到小算。这么说可能不太好理解,画个图一个就能看明白。直接上代码:
#include<iostream>
using namespace std;
int bg[1010];
int dp[1010];
int main()
{
int N;
memset(dp,0,sizeof(dp));
cin >> N;
for (int i = 1; i <= N; i++)
{
cin >> bg[i];
}
dp[0] = 1;
for (int j = 1; j <= N; j++)
{
for (int i = 40; i >= 1; i--)
{
dp[i] = dp[i];
if (i - bg[j] >= 0)
dp[i] += dp[i- bg[j]];
}
}
cout << dp[40] << endl;
return 0;
}
我接下来到蓝桥杯 比赛这段时间尽量每天做一道往年题目或者找到合适的发上来,一方面监督自己,一方面求大佬指正,希望一起进步一起学习哈!!!