【BHOJ DH的满k叉树】DP | 动归 | E

DH的满k叉树

URL: DH的满k叉树

时间限制: 1000 ms 内存限制: 204800 kb

总通过人数: 80 总提交人数: 85

 

题目描述

有一颗无穷深的满k叉树,且每一个节点与它的子节点相连的边的权值分别为1,2,…,k,DH现在想知道在这棵树上有多少条从根出发不重复经过边的路径满足经过的边权值之和为n而且至少经过一条权值不小于d的边

输入

多组输入数据,对于每一组输入数据,为一行,包括三个整数 n, k, d ( 1≤ n,k ≤100, 1≤ d ≤ k ) 

输出

对于每一组数据,输出一行,为该组数据答案对1000000007(1e9+7)取模的结果

输入样例

1 1 1

输出样例

1

分析

算是比较基础的 dp 了:

  1. 【定义】: dp[i][j] 表示 路径和为i、只选用每一层的第[1, j]边 的 方案总数

  2. 初始化dp[0][...] = 1(自然方案数 == 1)

  3. 【递推顺序】:外层循环第一维[1, MAX](从1开始增加路径和),内层循环第二维[1, MAX](逐渐扩大选择边的范围)

  4. 【状态转移方程】:dp[i][j] = sum { dp[i-k][j] } (k<=i,否则路径和不可能为i;同时k<=j,否则超出可选范围)

  5. 【答案】(容斥原理的思想):dp[n][k] - dp[n][d-1](即总方案数 - 只选比d小的边的方案数)(做了减法,还要记得 +MODD 再 %MODD)

 

AC代码 

#include <stdio.h>
#define sc(x) {register char _c=getchar(),_v=1;for(x=0;_c<48||_c>57;_c=getchar())if(_c==45)_v=-1;for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=getchar());x*=_v;}
#define se(x) {register char _c=getchar(),_v=1;for(x=0;_c<48||_c>57;_c=getchar())if(_c==45)_v=-1;else if(_c==-1)return 0;for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=getchar());x*=_v;}
#define PC putchar
void PRT(const int a){if(a>=10)PRT(a/10);putchar(a%10+48);}

#define MN 100
#define MODD 1000000007

int dp[MN+3][MN+3];

void table(void)
{
	int i, j, k;
	for (i=1; i<=MN; i++)
		dp[0][i] = 1;

	for (i=1; i<=MN; i++)
	{
		for (j=1; j<=MN; j++)
		{
			for (k=1; k<=i && k<=j; k++)
			{
				dp[i][j] += dp[i-k][j];
				dp[i][j] %= MODD;
			}
		}
	}
}

int main()
{
	table();
	int n, k, d;
	while (1)
	{
		se(n)sc(k)sc(d)
		PRT((dp[n][k] - dp[n][d-1] + MODD) % MODD), PC(10);
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值