hdu6787 1005-Chess 2020 年百度之星·程序设计大赛 - 初赛三

http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=891&pid=1005

http://acm.hdu.edu.cn/showproblem.php?pid=6787

设f[i][j],为可以恰好踩到到第j个位置,前面已经放了i个传送门的方案数

那么我们可以枚举j位置上一个可以恰好踩到的位置j-k,其中j-k+1到j-1是放满了传送门的,所以这些位置时不可以踩到的,只能踩到j-k,那么这些放传送门的地方传送到哪里都是无所谓的,方案数就是一个连乘

那么只要由f[i-(k-1)][j-k]转移到f[i][j]就行了,初始状态f[0][j]=1,也就是完全不放传送门的方案数就是1种

由于我们最后要恰好到踩到n,且放了m个传送门,所以答案是f[m][n]

每次转移都从上一个可以踩的地方j-k转移过来,更远的地方如果也有可以转移到j的位置l,不用管它,因为l到j-k中间有没有放已经被l->j-k给转移过了,所以统计的方案数是正确的

#include
#define pb push_back
using namespace std;
typedef long long ll;

const int maxl=1010;
const int mod=1e9+7;

int n,m,cas,k,cnt,tot;
int a[maxl],b[maxl];
ll dp[maxl][maxl],ans;
char s[maxl];
bool in[maxl]; 

inline void prework()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
		for(int j=1;j<=n;j++)
			dp[i][j]=0;
} 

inline void mainwork()
{
	for(int j=1;j<=n;j++)
		dp[0][j]=1;
	ll tmp=1;
	for(int i=1;i<=m;i++)
		for(int j=1;j<=n;j++)
		{
			tmp=1;
			for(int k=1;k<=11;k++)
			{
				if(j-k<=0 || k-1>i)
					break;
				dp[i][j]=(dp[i][j]+dp[i-(k-1)][j-k]*tmp)%mod;
				tmp=tmp*(j-k-1)%mod;
			}
		}
	ans=-1;
	if(dp[m][n]>0)
		ans=dp[m][n];
}

inline void print()
{
	printf("%lld\n",ans);
}

int main()
{
	int t=1;
	scanf("%d",&t);
	for(cas=1;cas<=t;cas++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

 

你可能感兴趣的:(DP)