前言
看到这道题以后第一反应是基础递归策略,但是被hack数据卡了时间以后才想到,用DP才能过,否则会被一直卡时间复杂度。毕竟递归下去的时间复杂度为
O
(
2
n
)
O(2^n)
O(2n).。
先放题目位置:P1164 小A点菜
下面这个是关键:
然后这里可以放一下这个hack数据:
所以,递归来做肯定是会TLE的
递归式的得出:
因为有两个变量,N,M这两个变量分别代表了有的菜品个数,以及手上有的钱;这样的话记f[N,M]为在第1到N道菜品中,手上有M元的时候一共可选的方案总数。初始当N=0或者M=0的时候,按照意义来说,是0,但是在M的0的额时候,N从0到n都应该置为1(这个要从后面递推的时候才能发现)。然后,对于f[N,M]考虑第N道菜品,它既可能取也可能不取,当取的时候,其刚好与f[N-1,M-a[N]]的方案数差不多(因为刚好取用完),当不取的时候,对应的方案数为f[N-1,M]。这样,可以得到递推关系为:
f
[
N
,
M
]
=
f
[
N
−
1
,
M
−
a
[
N
]
]
+
f
[
N
−
1
,
M
]
f[N,M]=f[N-1,M-a[N]]+f[N-1,M]
f[N,M]=f[N−1,M−a[N]]+f[N−1,M]
然后这里就可以看到,当m+a[N]的时候,M-a[N]为0,但是这是可以取的且方案数为1,所以,在之前的说明中将M=0的那一列置为1.通过这个递推式一直从
f
[
1
,
1
]
f[1,1]
f[1,1]计算到
f
[
N
,
M
]
f[N,M]
f[N,M]且最终的
f
[
N
,
M
]
f[N,M]
f[N,M]即为结果。
最后给出我的AC代码:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int N,M,i,j;
scanf("%d%d",&N,&M);
int a[N+1];
for(i=1;i<=N;i++)
scanf("%d",&a[i]);
int f[N+1][M+1];
for(i=1;i<=M;i++)
f[0][i]=0;
for(i=0;i<=N;i++)
f[i][0]=1;
for(i=1;i<=N;i++)
for(j=1;j<=M;j++){
if(j-a[i]<0)
f[i][j]=f[i-1][j]+0;
else
f[i][j]=f[i-1][j]+f[i-1][j-a[i]];
}
printf("%d",f[N][M]);
return 0;
}