链接: 2020 年百度之星·程序设计大赛 - 初赛三 1005
题意:
有一个长度为 n 的棋盘,你每次扔筛子可以向前移动 1-11格,你还可以在棋盘上设置 m 个传送器,传送到当前位置以前的位置,问有多少种传送器的摆放方式,使你可以到达 n 点。
思路:‘
- 分析一下什么情况下不能达到 n 点 ,因为传送器只能往前传,所以当这里有大于连续 10 个传送器时,他就无法到达 n 点。
- 所以可以设置 dp 状态为 dp[ i ][ j ][ k ],表示到第 i 个位置摆放了 j 个传送器 ,并且有 k 个连续的传送器的方案数。然后转移就很好写了。
- 开 ll 会爆内存 ,但 int 的话中间有个乘会超过 int 范围 ,所以用 ll 算一下在加 ,一次 dp 把所有答案都预处理出来,不用每次都算。
#include <iostream>
#include <cstdio>
#include <map>
#include <math.h>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 1;
const int mod=1e9+7;
int T,n,m;
int dp[maxn][maxn][11];
void solve(int n , int m){
for(int i = 1; i <= n; i++) dp[i][0][0] = 1;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= i; j++){
for(int k = 0; k <= 10; k ++){
dp[i][j][0] = (dp[i][j][0] + dp[i-1][j][k]) % mod;
if(k < 10){
ll x = (ll)dp[i-1][j-1][k] * (i-1) % mod ;
dp[i][j][k+1] = (dp[i][j][k+1] + x) % mod;
}
}
}
}
}
int main(){
scanf("%d",&T);
solve(1000,1000);
while(T--){
scanf("%d%d",&n,&m);
if(dp[n][m][0] == 0) cout<<-1<<endl;
else cout<<dp[n][m][0]<<endl;
}
}