题目链接
题目大意:给出你三个数x,y,z还有N,有N个位置,位置用来添加数字,要求连续位置上的数的和能够得到x,y,z不要求所有位置都用得到,求出所有的满足题意的序列的种类数,对1e9+7取模
思路:这个题目的思路还是很清奇的,有点好玩吧,
首先,我们将1=1,2=10,3=100,5=10000,也就是要注意后面0的个数,
可以这样说,我想表示x那么就可以表示为1后面共有x-1个0,
这样能表示我要添加上的每一个数的值,计算起来也比较方便,直接从后面加上就可以了,
然后就是准备好枚举每一个数就可以了,因为前面的情况还是很多的所以我就直接减去了他不符合的情况,
还有一个很关键的点,就是包含,
比如说,我们现在的5=10000,我们可以搞成2+3=10100 4+1=10001 看上去的话,我们可以发现他们和我们需要的5都是满足包含关系的,其实说白了就是该有的几个点你肯定要有,其他的就是我们找到的情况,没错,好好想想
解释一下状态转移方程吧,
DP[i][j&S]也就表示了添加到第i个数,然后数为j&S时候不符合的情况,下面代码中有解释,可以看看。。。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int mod=1e9+7;
int dp[50][1<<17];
int fow,S,ans;
int x,y,z;
int n;
int main()
{
scanf("%d",&n);
scanf("%d%d%d",&x,&y,&z);
ans=1;
for(int i=1;i<=n;i++)
ans=1ll*ans*10%mod;
S=(1<<x+y+z)-1;
fow=(1<<x-1)|(1<<x+y-1)|(1<<x+y+z-1);
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=S;j++)//枚举我在添加第n个数时候前n-1个数的所有可能
{
for(int k=1;k<=10;k++)//枚举每一种可能
{
int T=(j<<k)|(1<<k-1);//相当于是从后面拼接上一个我需要加上的数
T&=S;
if ((T&fow)!=fow) dp[i][T]=(dp[i][T]+dp[i-1][j])%mod;//对不上号也就是不符合的情况,也就是上面提到的包含的情况
}
}
}
for(int i=0;i<=S;i++)
ans=(ans-dp[n][i]+mod)%mod;
printf("%d\n",ans);
}