题目大意:
给你一个具有衰变值的例子,以及几堵墙,一个衰变值为n的粒子每穿过一堵墙,并且会向相反方向分裂出一个衰减值为n-1的粒子,作为一个新的粒子,给你m堵墙,以及一个衰减值为n的粒子,问你最后一共有多少个粒子,当粒子的衰减值为1时,就不会在分裂新的粒子了。
解题思路:
首先这是cf 的C题,那么应该是涉及到一些基本算法的,大概率是贪心或者dp,那么这个题,题目给了我们一个规则,大致可以根据这些规则,推导出一个递推方程,难点在于对于方向的处理。可以这么想,我们要得到的是最后通过m堵墙的答案,那么我们就dp通过的墙数以及粒子的衰变值,而不是去递推一堵堵具体的墙。
递推方程为:
dp[i][j]=(dp[i-1][m-j]+dp[i][j-1])%mod;
解释一下这个递推方程,衰变值为i的粒子,以及j堵墙,dp[i][j]表示的其实是,一开始衰变值为i的粒子,还要通过j堵墙之后产生的粒子数,一个粒子,通过一堵墙的话,会向相反方向分裂出一个值为j-1的粒子,而这个粒子会通过m-j堵墙,所以是dp[i-1][m-j],同时还会直接穿过墙,继续通过剩下的j-1堵墙,所以dp[i][j]=(dp[i-1][m-j]+dp[i][j-1])%mod;
代码:
#include<bits/stdc++.h>
using namespace std;
long long dp[1005][1005];///dp[i][j],i表示衰变值,j表示还要穿过的墙数
const int mod = 1e9 + 7;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
///一开始没有看懂别人的题解,后来发现, 原来在穿墙方向上,dp是不用做出处理的,只需要表示需要还要穿过的墙数就行了
///这样一来,也就没有所谓的正反之分了
int t ,n,m;
cin>>t;
while(t--){
cin>>m>>n;
for(int i=1;i<=m;i++)///对衰变值为1的点初始化,因为这种点无论在什么位置,产生的数量只有1
dp[1][i]=1;
for(int i=1;i<=n;i++)///对墙数为0进行初始化,这部分无论衰变值是多少,都是1
dp[i][0]=1;
for(int i=1;i<=n;i++)///分裂是有先后顺序的,所以先遍历衰变值,把一个粒子衰变完成,再讨论其他粒子
for(int j=1;j<=m;j++)
dp[i][j]=(dp[i-1][m-j]+dp[i][j-1])%mod;
cout<<dp[n][m]<<endl;
}
}