codeforces 431C k-Tree 动态规划

题目链接 http://codeforces.com/contest/431/problem/C

题意,有个人想出了一颗奇特的树,首先,这颗树是个满k叉树(每个节点都有k个子节点)。其次,该树的深度为无穷。再者,每个节点的k条边,权重为1,2,3....k,各一条。然后求从树根出发的路径的数量,首先路径上的权重正好为n。并且,该路径经过的边上,至少有一条边的权重不小于d,求这样的路径有多少条。(n,d,k不超过100)

第一个样例子答案为3,分别为1+2,2+1,3

 

 

 

 

 

 

题目思路:

首先,如果假设k为无穷大。并且没有d的限制条件。

我们设数组dp[i]为从根出发的路径,权重和为i的路径条数。

然后观察这颗树会发现,除掉这个根节点。产生k棵树,发现这k棵树与原来的树完全一样。

所以dp[i]也同样适用于每个子节点。

我们知道初始状态dp[1]= 1。即权重和为1的树只有1条。

然后dp[2] = 2; 产生它有两种情况。

从路径长度为1的出来,然后到下一个节点,然后下一个节点到以后组成的权重为1的条数也为dp[1]

然后再加上长度为2自己原来的一条。

dp[2] =  = 1+dp[1]

同理

dp[3] =   1+dp[1]+dp[2]

dp[4] = 1 +  dp[1] + dp[2] + dp[3] 

dp[n] = 1 + dp[1] + dp[2] + ...+ dp[n-1];

然后,不如为了方便计算,我们设 dp[0] = 1;

则dp[n] = dp[0] + ... + dp[n-1];

以上是k很大的时候成立的。

接着,增加个限制条件k,不为无穷大。

所以dp[n] = dp[n-k] +... dp[n-1]

即 路径长为n的条数,必然为,从它衍生出来的k条里每条1 * dp[n-该条的权重];

最后,再解决d的问题。也就是说我们需要保证区分出dp中哪些条数已经含有了权重不小于d的。

我们只要再把dp提高一个维度,用另一个维度表示到该边为止是否已经出现过权重不小于d的边。

这样,再稍微改变一下状态转移方程。

其中dp[maxn][2];

对其中权重为j的边来说 dp[i][0] 里值来自于 dp[i-j][0]

dp[i][1] 来自于 当j>=d 时的dp[i-j][0]和dp[i-j][1]

至此,思路已经有了,接下来就是代码了。

#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long LL;
typedef long long ll; 
const int maxn = 1e6+6;
const int INF = 0x3f3f3f3f;
const int modn = 1e9+7 ;
char str[maxn];
int a[maxn];
ll dp[maxn][2];
void show(int a[],int n){
	for(int i=0;i<n;i++){
		printf("%d ",a[i]);
	}printf("\n");
}
int main(){
//	freopen("C:\\Users\\lenovo\\Desktop\\data.in","r",stdin);
	int n,k,d;
	while(scanf("%d%d%d",&n,&k,&d)+1){
		memset(dp,0,sizeof(dp));
		
		dp[0][0]= 1;
		for(int i=1;i<=n;i++){
			
			for(int j=1;j<=i&&j<=k;j++){
			
				 
				if(j>=d){
			
					dp[i][1]+= dp[i-j][0]+dp[i-j][1];

				}else{
					dp[i][1]+= dp[i-j][1];
					dp[i][0]+=dp[i-j][0];
				}
				dp[i][0]%=modn;
				dp[i][1]%=modn;
			}
			
		}

		printf("%I64d\n",dp[n][1]);
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值