小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;
}
posted @ 2020-11-14 16:53  凌曦月lx  阅读(70)  评论(0编辑  收藏  举报