给定三个整数n
,k
和d
,n
可以将多少种方式表示为正整数i<=k
的总和,这样d
1}}在总和中至少出现一次。谨慎0<d<=k
。我的方法是递归的;
#include <stdio.h>
#include <stdlib.h>
int n,k,d,ans=0;
void solve(int totw,int flag)//totw is the total sum till now, and flag is to keep track of the number of d's in the sum.
{
if(totw>n)
return;
if(totw==n && flag>0)//flag>0--->at least 1 d
{
ans = (ans+1)%1000000007;//answer is expected modulo 10^9+7
return;
}
int i=1,h=k;
if(h>n-totw)
h=n-totw;
while(i<=h)
{
if(i==d)
flag++;
solve(totw+i,flag);
i++;
}
}
int main()
{
scanf("%d %d %d",&n,&k,&d);
solve(0,0);
printf("%d",ans);
}
输入:
3 3 2
输出:
2
但法官显示Time Limit Exceeded
。在这种情况下,还有更有效的算法吗? 0<n,k<=100
PS:我只是想知道是否有任何组合论证可以在没有recursion
或iteration
的情况下解决这个问题。是的.... 总和顺序 重要。
答案 0 :(得分:1)
您可以将递归调用表示为图形。每个节点都由函数(totw, flag)
的参数给出,并且每当进行递归调用时都会添加边。此图表中有大约n*2
个节点和n*2*k
边。
此图表有一个非常有趣的属性:它是DAG(例如因为totw
在每次调用时严格增加)。因此,当您调用solve(totw, flag)
时,可以将结果保存在数组中,这样就不会计算两次。
这实际上是一种解释dynamic programming的方法:一种在DAG中找到最短路径的算法(稍作修改,它也可以计算最长路径/路径数,但你明白了)。图G = (V, E)
上的时间复杂度为O(|V|+|E|)
(实际上是amortized analysis种类。)
因此,将结果保留在数组中将导致O(nk)
时间复杂度。
实际上,您可以通过略微更改图表来缩短实施时间:
enum { UNKNOWN = -1 };
/* not tested */
/* solve(num, d_seen): returns the answer to the problem with a sum
target, knowing whether `d` was already used */
int solve(int sum, int d_seen)
{
if (sum == 0) return d_seen;
int ans = dp[sum][d_seen];
if (ans == UNKNOWN) {
ans = 0;
/* min(a, b) being what it is supposed to be */
for (int i = 1; i <= min(sum, k); ++i)
ans = (ans + solve(sum - i, d_seen || (i == d))) % MOD;
}
return (dp[sum][d_seen] = ans);
}
顺便说一下,在递增它之后你不会将flag
重新置于0。
答案 1 :(得分:0)
参考本维基百科页面:Stars and Bars
基本上,将n
分成k
个正整数的方法的数量为(n-1)C(k-1)
或n-1
选择k-1
。因此,您可以将k
= 1
迭代到n-d-1
,以查找可以将n-d-1
拆分为任意数量的正整数的方式。
请参阅here,了解如何有效地为大型nCk
和n
高效计算k
答案 2 :(得分:0)
当然,一些组合数学可以在不使用代码的情况下解决这个问题。但是递归挑战看起来很有趣。
经过轻微测试的例子 - 效率不比OP高。
(步骤1,使用更有意义的变量名称)
unsigned long solve(unsigned sum_n, unsigned max_value_k, unsigned special_d) {
if (sum_n == 0) return special_d == 0;
unsigned long solutions = 0;
// Improve efficiency, only loop up to min(k,n)
if (max_value_k > sum_n) {
max_value_k = sum_n;
}
// Will the loop contain d?
if (max_value_k >= special_d) {
for (unsigned i = 1; i <= max_value_k; i++) {
solutions += solve(sum_n - i, max_value_k,
i == special_d ? 0 : special_d);
solutions %= 1000000007;
}
}
return solutions;
}
#include <stdio.h>
int main(void) {
unsigned n, k, d;
for (n=0; n<100; n++) {
printf("%u %lu\n", n, solve(n, 100, 1));
fflush(stdout);
}
scanf("%u %u %u", &n, &k, &d);
printf("%lu\n", solve(n, k, d));
}