【题解】种树的艺术

题目

题目描述

有N棵高度不一样的树要种成一行,为了让种树更加有艺术性,制定一个种树规则,希望从左边看过去只能看到L棵树,从右边看过去只能看到R棵树,请问有多少种不同的种树方案。

输入格式

输入包含多组数据。

首先第一行包含一个整数t,表示数据的组数。

之后t行,每行包含三个数N,L,R,以空格隔开,表示树的棵数N以及从左边看过去的棵数L和从右边看过去的棵数R。

输出格式

共t行,每行一个数,表示每组数据所对应的种树的方案数。答案可能很大,请对 998244353取模。

样例

样例输入

2
4 1 2
4 2 1

样例输出

2
2

数据范围与提示

样例分析:

如:对于“4 1 2”这种情况而言,存在两种种树的方案,分别为{4,1,2,3},{4,2,1,3}。

数据范围:

对于30%的数据:1≤T≤5;1≤n≤10。

对于100%的数据:1≤T≤1000;1≤n≤200。


思路

一.状态

dp[i][j][k]表示i棵树从左边看有j棵,从右边看有k棵 的方案数

转移

我们知道状态的转移主要是靠前面已知的状态来转移后面的状态,现在考虑一个状态 d p [ i ] [ j ] [ k ] dp[i][j][k] ,对于这道题而言,想靠一个更小的 i i 来转移现有的 i i ,可以考虑为在之前的状态中再种几棵树,当然也可以理解为拔掉几棵树,那这样理解肯定是每次少考虑变化的数量更好啦~,那我们就不妨考虑 i 1 i-1 吧:

考虑拔掉最矮的树:(至于为什么要选它,看后面)

1.最矮的树在最左边,则如果没有它从左边看的树就会减一(因为从右边看的肯定会被遮住,拔不拔它都没关系) 
2.最矮的树在最右边,则如果没有它从右边看的树就会减一(同上↑) 
3.最矮的树在中间(既不在最左边又不在最右边),则从左右看树都不会受影响(因为它是最矮的,所以哪怕它在也一定会其他高的树被遮住,看不到,拔掉它时也就一样了)
#include<cstdio>
#include<iostream>
using namespace std;
const int MAXN=205;
const int MOD=998244353; 
long long dp[MAXN][MAXN][MAXN];//dp[i][j][k]表示i棵树从左边看有j棵,从右边看有k棵 的方案数 
int main(){
	int t;
	scanf("%d",&t);
	int n;
	int l,r;
	dp[1][1][1]=1;
	for(int i=2;i<=200;i++){//提前预处理好,避免输入一次算一次 
		for(int j=1;j<=i;j++){
			for(int k=1;k<=i-j+1;k++){
				dp[i][j][k]=(dp[i-1][j-1][k]/*1*/+dp[i-1][j][k-1]/*2*/+dp[i-1][j][k]*(i-2)/*3(因为在中间有i-2个位置,所以要乘i-2)*/)%MOD;
			}
		}
	}
	while(t--){
		scanf("%d",&n);
		scanf("%d %d",&l,&r);
		printf("%lld\n",dp[n][l][r]);
	}
return 0;
}

猜你喜欢

转载自blog.csdn.net/tanfuwen_/article/details/106864756