题目链接 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]);
}
}