我们定义dp[i][j]为第i个位置放置第j个数的方案数
当我们遍历到位置i时,如果i<=a[j],那么我们的状态转移方程完全可以直接由上一个位置转移过来
dp[i][j]+=dp[i-1][k] 1<=k<=m
当i>a[j]时,就必须考虑连续性问题了 i==a[j]+1时,我们加上前一位全部状态时,其实也包含进去了1~a[j]位置全是j的情况,这种情况个数为1,所以减去1
i==a[j]+2时, 我们加上前一位全部情况时,会有破坏连续性的情况出现,当且仅当i往前推a[j]个位置全部是j的情况时,我们再加上位置i是j才会破坏连续性,所以我们减去这种情况的方案数即可,那么这种方案数多少?dp[i-a[j]-1][k] 1<=k<=m&&k!=j 因为i-a[j]-1之后全部相同,所以只需考虑在这之前的方案数即可,另外,当这一位置是j时,还需要减去吗?那就要看我们加dp[i-1][k]时有没有包含这种情况就行了,dp[i-1][k]其实就是i==a[j]+1的情况,那时候我们就排除了1--a[j]都是j的情况,故并没有包含在内。
但是这样嵌套三层循环是不对的,会超时,所以我们应该进行一些前缀和处理
# include<cstdio>
using namespace std;
typedef long long ll;
# define mod 1000000007
unsigned int dp[7000+10][7000+10];
ll a[7010];
ll presum[7010];
int main ()
{
ll i, j, k;
ll n,m;
scanf("%lld%lld", &n, &m);
for (i = 1; i <= m; i++)
scanf("%lld", &a[i]);
if (m == 1 && a[1] < n)
{
printf("0");
return 0;
}
for( i=1;i<=m;i++)
{
dp[1][i]=1;
}
presum[1]=m;
for(i=2; i<=n; i++)
{
for(j=1; j<=m; j++)
{
dp[i][j]=presum[i-1];
if (i > a[j])
{
if (i == a[j] + 1)
dp[i][j] = (dp[i][j] - 1 + mod) % mod;
else
dp[i][j] = (dp[i][j] - (presum[i - a[j] - 1] - dp[i - a[j] - 1][j]) + mod) % mod;
}
}
for (j = 1; j <= m; j++)
presum[i] = (presum[i] + dp[i][j]) % mod;
}
printf("%lld", presum[n]);
return 0;
}