原题地址:P1164 小A点菜 - 洛谷
em…这道题写的是真的让我难受,本来最近在学习dp,于是打算找点dp的题练一下…
于是乎这个题代码我就用dp(自认为的dp)写出来了,然后也很顺利的通过了样例。
于是喜得AC 代码
#include <stdio.h>
int sum = 0;
void dp(int* a, int m, int n);
int main(){
int m, n;
scanf("%d %d", &m, &n);
int a[500];
int i;
for(i = 0; i < n; i++)
scanf("%d", &a[i]);
dp(a, m, n - 1);
printf("%d", sum);
return 0;
}
void dp(int *a, int m, int n){
if(m == 0){
sum++;
return;
}
else if(n < 0)
return;
else if(a[n] > m){
dp(a, m, n - 1);
return;
}
else{
dp(a, m, n - 1);
dp(a, m - a[n], n - 1);
return;
}
return;
}
然后洛谷oj很现实的给了我一巴掌,还是狠狠的一巴掌…
于是博主我选择了最偷懒的方法 最棒的方法,选择去看题解
或许是老天爷嫌弃我太懒了,只找到c++,java,python语言写的题解。。。难受,但也只能将就去看了。。。
看完别人代码后,发现自己对背包问题理解太浅了。。。于是乎又去找了讲解背包问题的博客,很幸运找到了一个讲的很全面的博客,嘿嘿嘿!这里附一下连接,有兴趣的朋友可以去看看:背包问题九讲笔记_01背包
好了,看完这个博客后,就开始对本题这个背包问题进行分析。
首先这个问题是在刚好花完钱的情况下,有几种点餐方案。那么我们建立一个f[i][j] 的二维数组。i,j表示在刚好花完j元,有i道菜,有多少种点餐的情况。我们令f数组初始化为0,代表最初都是0钟方案。
然后有以下三种情况:
-
if(j==第i道菜的价格)f[i][j]=f[i-1][j]+1;
这种情况是,新添加的一道菜的价格如果刚好和我包里的总钱一样,那么就可以在之前的方案上添加一个方案(所有钱都来买这一道菜), 所以f[i][j] = f[i-1][j] + 1; -
if(j>第i道菜的价格) f[i][j]=f[i-1][j]+f[i-1][j-第i道菜的价格];
这种情况是,我买了新添加的一道菜后,钱还有剩余,然后剩余的钱可以点餐的方案和f[i-1][j-第i道菜的价格]的点餐方案一样多,再加上我不买这道菜的点餐方案为f[i][j],那么加上这道菜后的点餐方案为我买这道菜和不买这道菜的方案加起来的总和。 -
if(j<第i道菜的价格) f[i][j]=f[i-1][j];
这种情况是,新添加的一道菜的价格比我我包里的总钱还多,就算我用所有钱来买这道菜也买不起,那么这个时候我只能对这道菜进行不选,于是方案和少了这份菜的点餐方案一样多
这个时候我们就只需要对f数组从头到尾依次循环一次就行了,这样就能得到不同的菜数和总钱的点餐方案了,循环完了之后我们只需要再讲f数组最后的一个数输出就可以了。
于是乎,顺利ac。
AC代码
#include <stdio.h>
int a[101], f[101][10001] = {0};
int main()
{
int n, m, i, j;
scanf("%d%d", &n, &m);
for(i = 1; i <= n; i++)
scanf("%d", &a[i]);
for(i = 1; i <= n; i++)
for(j = 1; j <= m; j++)
{
if(j == a[i])
f[i][j] = f[i - 1][j] + 1;
else if(j > a[i])
f[i][j] = f[i - 1][j] + f[i - 1][j - a[i]];
else if(j<a[i])
f[i][j] = f[i - 1][j];
}
printf("%d", f[n][m]);
return 0;
}
但是,我们的故事还没有结束!
由于看了前文提到的那篇博客,我知道这种背包问题可以从二维转化为一维,然后就继续做出了新的尝试。
对于一维而言,其实就算不断的覆盖之前存储的数据。
但由于i-1时的数据时i时计算的根本,所以要解决这个问题的方法就算i 正序循环,j 逆序循环。因为f[i][j] = f[i - 1][j] + f[i - 1][j - a[i]];只会调用上一层小于或等于本层价格的数,如果我们倒序来就不会把我们将会使用的数字给覆盖了。
但这个时候由于计算方式改变,要初始化f[0] = 1(代表0元0个菜,有一种点餐方式),其他为0。
于是乎再次得出AC代码
AC代码
#include <stdio.h>
#define N 110
int n, m, a[N],f[10010];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
f[0]=1;
for(int i=1;i<=n;i++)
for(int j=m;j>=a[i];j--)
f[j]=f[j]+f[j-a[i]];
printf("%d", f[m]);
return 0;
}