小A点菜
思路\(1\)
十分经典的一道DP题。
转移方程式为:
f[j]+=f[j-a[i]];
我们首先定义\(f[i][j]\)表示前\(i\)个菜品恰好花费\(j\)元的方案数。
当我们买第\(i\)道菜时:
f[i][j]+=f[i-1][j-a[i]];
当我们不买第\(i\)道菜时:
f[i][j]+=f[i-1][j];
但当第\(i\)道菜的价格被就是\(j\)时,就可以只买自己,这种情况应该其实包含在\(1\)中,但是由于\(f[i][0]=0\),所以不会算入这种情况。所以我们就要把\(f[i][0]\)刚开始就赋值为\(1\)。
代码
#include <bits/stdc++.h>
using namespace std;
int n,m,a[105],f[105][10005];
int main() {
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=0;i<=n;i++) f[i][0]=1;//注意0
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
f[i][j]+=f[i-1][j];
if(j>=a[i]) f[i][j]+=f[i-1][j-a[i]];
}
}
printf("%d",f[n][m]);
return 0;
}
我们可以优化为一维。
但我们的第二重循环必须逆序跑,因为我们一维的转移方程式为:
f[j]+=f[j-a[i]];
如果跑正序,我们的\(f[j]\)还没有更新,\(f[j-a[i]]\)反倒先更新了,这样的话与题意的“每种菜只有一份”相冲。如果没有这句话,我们就可以正序了。
#include <bits/stdc++.h>
using namespace std;
int n,m,a[105],f[10005];
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-a[i]];
}
printf("%d",f[m]);
return 0;
}
思路\(2\)
\((1)if(j==第i道菜的价格)\ f[i][j]=f[i-1][j]+1;\)
\((2)if(j>第i道菜的价格)\ f[i][j]=f[i-1][j]+f[i-1][j-第i道菜的价格];\)
\((3)if(j<第i道菜的价格)\ f[i][j]=f[i-1][j];\)
说的简单一些,这三个方程,每一个都是在吃与不吃之间抉择。若钱充足,办法总数就等于吃这道菜的办法数与不吃这道菜的办法数之和;若不充足,办法总数就只能承袭吃前\(i-1\)道菜的办法总数。依次递推,在最后,我们只要输出\(f[n][m]\)的值即可。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,a[105],f[105][10005];
int main() {
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) {
for(int 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 f[i][j]=f[i-1][j];
}
}
printf("%d",f[n][m]);
return 0;
}