题意:n个平面和一个粒子的衰变期为k,粒子每经过一个平面除了可以直接穿过平面外还会朝着反方向产生一个衰变期-1的粒子,如果衰变期为1则不会产生粒子。问最后有多少个粒子。
思路:dp。我们用dp[i][j]表示该粒子前方还有i个平面且当前衰变期为j时的粒子数。所以答案就是dp[n][k]。分析下状态转移,如果衰变期为1,不管有几个平面答案都是1,即dp[i][1]=1;如果粒子前面没有平面了,那它就不能分裂所以答案也是1,即dp[0][x]=1;考虑到粒子穿过一个平面后有一种情况是衰变期不变前面的平面数量减一,即dp[i-1][j];还有一种情况是穿过一个平面后衰变期减一然后反向,此时对反向粒子来说它前面还有n-i个平面,即dp[n-i][j-1]。所以dp[i][j]=dp[i-1][j]+dp[n-i][j-1]。答案可能很大记得取模。
注意:先枚举k再枚举n,因为根据 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ n − i ] [ j − 1 ] ,如果先枚举n的话,子状态是i-1与n-i,在n-i>i的时候dp[n-i][j-1]还没被处理到,所以这种转移不可行;反之如果先枚举k的话,dp[i-1][j]可以按顺序转移到dp[i][j],dp[n-i][j-1]的话则是上一个j处理的答案,所以不会产生冲突。具体例子:假如n=4,k=4的时候,按照先枚举n,dp[1][3]=dp[0][3]+dp[3][2],在这个时候,dp[3][2]这个状态还没有维护到。
代码:
#include <bits/stdc++.h>
#define MOD 1000000007
typedef long long ll;
using namespace std;
const int N = 1e3+5;
ll dp[N][N];

int main()
{
	int t; cin >> t;
	while(t--)
	{
		int n,k; cin >> n >> k;
		for(int i=0;i<=n;i++)
		{
			for(int j=0;j<=k;j++) dp[i][j]=0;
		}
		for(int i=1;i<=k;i++) dp[0][i] = 1;
		for(int i=0;i<=n;i++) dp[i][1] = 1;
		for(int j=1;j<=k;j++)
		{
			for(int i=1;i<=n;i++)
			{
				dp[i][j]=(dp[i-1][j]+dp[n-i][j-1])%MOD;
			}
		}
		cout << dp[n][k] << endl;
	}
	return 0;
}