输入N(1<= N <= 1000)
输出幸运号码的数量 Mod 10^9 + 7
1
9
思路:
1、计数问题,考虑dp:
①我们肯定是根据数位进行dp,那么设定dp【i】【j】表示现在一个数,一共长度为i,和为j的方案数。
②那么不难推出其状态转移方程:
dp【i】【j】=dp【i】【j】+dp【i-1】【j-k】,其中k表示当前第i位最后一个数字为k。
③对于状态转移过程中,注意去掉前导0的情况发生。
2、那么我们答案就是Σdp【n】【i】*dp【n】【i】?
很显然不是,因为我们要得到的是长度为2n的一个数,而非两个不含前导0的部分连接在一起,我们只要保证前边n个数所组成的方案中,不包含前导0即可,后半部分是可以有前导0存在的,所以我们再设定一个dp2【i】【j】,状态转移方程一样,只是这个部分我们不对前导0进行处理。
那么答案就是:Σdp【n】【i】*dp2【n】【i】(1<=i<=n*9);
3、因为我们设定的数组偏大,而又考虑到第i位只和第i-1位方案数有关,那么我们考虑用滚动数组进行空间优化。
另外,因为思路比较挫,暴力姿势如果不太好的话会TLE掉、注意暴力姿势。
4、其实这个题打表也是个很好的选择,暴力Dp反正也不会多久,跑个表直接打印粗来。。。。(继续膜效率榜第一的方法.................)
Ac代码:
#include<stdio.h>
#include<string.h>
using namespace std;
#define mod 1000000007
#define ll __int64
ll dp[2][9050];
ll dp2[2][9050];
int main()
{
int n;
while(~scanf("%d",&n))
{
memset(dp,0,sizeof(dp));
memset(dp2,0,sizeof(dp2));
dp[0][0]=1;
dp2[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=i*9;j++)
{
for(int k=0;k<=9;k++)
{
int q=j-k;
if(q>=0)
dp2[1][j]=(dp2[1][j]+dp2[0][q])%mod;
if(i==1&&k==0)continue;
if(q>=0)
dp[1][j]=(dp[1][j]+dp[0][q])%mod;
}
}
for(int j=0;j<=i*9;j++)
{
dp[0][j]=dp[1][j],dp2[0][j]=dp2[1][j];
dp[1][j]=0,dp2[1][j]=0;
}
}
ll output=0;
for(int i=1;i<=n*9;i++)
{
output=(output+(dp[0][i]*dp2[0][i])%mod)%mod;
}
printf("%I64d\n",(output+mod)%mod);
}
}