【2023.03.17】T3 题解

1.题意描述:

一个无限大的树,从根节点开始,每个节点都有k个孩子,对应边权分别为1到k。

问从根开始,有多少条路径满足以下条件:

  1. 路径中至少包括一条权重大于等于d的边。

  2. 路径的边权总和为n。

由于答案可能很大,输出对1e9+7取模的结果。

2.输入输出格式

输入:

第一行:包含三个整数:n,k,d(1≤n,k≤100;1≤d≤k)中间用空格分隔

输出

输出方案数模1e9+7

3.样例

样例输入

3 3 2

样例输出

3

4.数据范围

对于100%的数据,1≤n,k≤100,1≤d≤k

一道考试题(但我爆零了)……

5.思路

  1. 定义二维数组dp[2][110]:(废话,做题的都会
	dp[0][i]

代表最大边权小于d且边权和为i时的方案数

	dp[1][i]

代表最大边权大于等于d且边权和为i时的方案数

  1. 来个特殊判断初始化:(废话,不就是初始化吗?都会
	dp[0][0]=1; 

这种情况指路径只有根节点的情况

	dp[1][0]=0;

因d不存在等于0的情况,故d[1][0]为0

  1. 输入:(废话,一年级学生都会
	cin>>n>>k>>d;

路径要求总权值为n有边权值大于等于d的路径为合法

每个节点有k个子节点,第几个子节点,父节点连接它的权值就是几

  1. 状态转移方程:(废话,用DP是个人都能看出来
	dp[0][i]=(dp[0][i]+dp[0][i-j])%m;

让 dp[0][i] 加上 dp[0][i-j] 的总方案数;

i-j指总边权值i减去现枚举的单边权值j得到的权值因为dp[0][i-j]所有方案数在前面得到了,所以在此路径末加上一条边,对总方案数无影响;

至于为什么不用考虑把节点插入中间,是因为这种情况会出现在别的 dp[0][i-j] 里(最好自己手模一遍)

	dp[1][i]=(dp[1][i]+dp[j<d][i-(j++)])%m;

j++让上面式子j遍历1k(k比i大时)或1i次,i-j为i-1~0;

让下面式子j遍历2k+1(k比i大时)或2i+1次,i-j为i-2~-1;
如果j<d时返回1,j就不满足为大于等于d的边就会向前加上dp[1][几],dp[1][几-1]。。。

(解释:如果说前面 dp[1][几]有方案,说明已经有大于等于d的的边 而在此路径末加上一条边,对总方案数无影响。反之则加上0)

如果j<d时返回0,j就满足为大于等于d的边就会向前加上dp[0][几],dp[0][几-1]。。。

(解释:j满足为大于等于d的边,说明有了大于等于d的边而在没有大于等于d的边的路径末加上一条j,不但对总方案数无影响,而且可以变为满足条件的路径)

  1. 输出:(废话,小学生都会
	cout<<dp[1][n];

输出路径中至少包括一条权重大于等于d的边路径的边权总和为n时的总方案数

6.代码

这是带注释的:

#include <bits/stdc++.h>
using namespace std;
const long long m=1e9+7;
long long dp[2][110];											//dp[0][i]代表最大边权小于d且边权和为i时的方案数 
 					 											//dp[1][i]代表最大边权大于等于d且边权和为i时的方案数
long long n,k,d;
int main(){
	dp[0][0]=1;													//此情况指路径只有根节点的情况
	dp[1][0]=0;													//因d不存在等于0的情况,故d[1][0]为0
	cin>>n>>k>>d;												//路径要求总权值为n有边权值大于等于d的路径为合法;每个节点有k个子节点,第几个子节点,父节点连接它的权值就是几
	for(int i=1;i<=n;i++){										//枚举总边权值为i=1到i=n的情况下 
		for(int j=1; j<=k&&j<=i;){								//枚举所有不大于i的有的单边权值 
			dp[0][i]=(dp[0][i]+dp[0][i-j] )%m;
																//让dp[0][i]加上dp[0][i-j]的总方案数;i-j指总边权值i减去现枚举的单边权值j得到的权值,因为dp[0][i-j]所有方案数在前面得到了而在此路径末加上一条边,对总方案数无影响 
																//至于为什么不用考虑把节点插入中间,是因为这种情况会出现在别的dp[0][i-j]里(最好自己手模一遍)
			dp[1][i]=(dp[1][i]+dp[j<d][i-(j++)])%m; 
																//j++让上面式子j遍历1~k(k比i大时)或1~i,i-j为i-1~0;让下面式子j遍历2~k+1(k比i大时)或2~i+1,i-j为i-2~-1;
																//如果j<d时返回1,j就不满足为大于等于d的边就会向前加上dp[1][几],dp[1][几-1]。。。 (解释:如果说前面 dp[1][几]有方案,说明已经有大于等于d的的边 而在此路径末加上一条边,对总方案数无影响。反之则加上0)
																//如果j<d时返回0,j就满足为大于等于d的边就会向前加上dp[0][几],dp[0][几-1]。。。 (解释:j满足为大于等于d的边,说明有了大于等于d的边而在没有大于等于d的边的路径末加上一条j,不但对总方案数无影响,而且可以变为满足条件的路径)
		}  
	}
	cout<<dp[1][n];												//输出路径中至少包括一条权重大于等于d的边且路径的边权总和为n时的总方案数 
	return 0;
}

这是方便童鞋们借(chao)鉴(xi)的:

#include<bits/stdc++.h>
using namespace std;
const long long m=1e9+7;
long long dp[2][110];
long long n,k,d;
int main(){
	dp[0][0]=1;
	dp[1][0]=0;
	cin>>n>>k>>d;
	for(int i=1;i<=n;i++){
		for(int j=1; j<=k&&j<=i;){
			dp[0][i]=(dp[0][i]+dp[0][i-j])%m;
			dp[1][i]=(dp[1][i]+dp[j<d][i-(j++)])%m; 
		}  
	}
	cout<<dp[1][n];
	return 0;
}

这是要求改码风的:

#include <bits/stdc++.h>

using namespace std;

const long long m = 1e9 + 7;

long long dp[2][110] , n , k , d;

int main()
{
	dp[0][0] = 1;
	dp[1][0] = 0;
	cin >> n;
	cin >> k;
	cin >> d;
	for (int i = 1;i <= n;i++)
	{
		for (int j = 1;j <= k && j <= i;)
		{
			dp[0][i] = (dp[0][i] + dp[0][i - j]) % m;
			dp[1][i] = (dp[1][i] + dp[j < d][i - (j++)]) % m; 
		}  
	}
	cout << dp[1][n];
	return 0;
}

题外话:

又蒻事儿还多,快来扁他

By 1156436222tjj

posted @ 2023-03-17 17:10  Hzzxx  阅读(101)  评论(0编辑  收藏  举报