【2023.03.17】T3 题解
1.题意描述:
一个无限大的树,从根节点开始,每个节点都有k个孩子,对应边权分别为1到k。
问从根开始,有多少条路径满足以下条件:
-
路径中至少包括一条权重大于等于d的边。
-
路径的边权总和为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.思路
- 定义二维数组dp[2][110]:(
废话,做题的都会
dp[0][i]
代表最大边权小于d且边权和为i时的方案数
dp[1][i]
代表最大边权大于等于d且边权和为i时的方案数
- 来个
特殊判断初始化:(废话,不就是初始化吗?狗都会
dp[0][0]=1;
这种情况指路径只有根节点的情况
dp[1][0]=0;
因d不存在等于0的情况,故d[1][0]为0
- 输入:(
废话,一年级学生都会
cin>>n>>k>>d;
路径要求总权值为n有边权值大于等于d的路径为合法;
每个节点有k个子节点,第几个子节点,父节点连接它的权值就是几
- 状态转移方程:(
废话,用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,不但对总方案数无影响,而且可以变为满足条件的路径)
- 输出:(
废话,小学生都会
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
本文来自博客园,作者:Hzzxx,转载请注明原文链接:https://www.cnblogs.com/xinao2186182144/p/17227474.html