求大神指导这个问题的算法

[复制链接]
查看11 | 回复3 | 2021-1-27 05:07:22 | 显示全部楼层 |阅读模式
已知N个大小在1至K的数的和是M,求有多少种N的结果,这个算法如何算有大神指导么
已知N>1,K>1,N*K>M>N
publicstaticintgetResult(intn,intk,intm){
}

分 -->
回复

使用道具 举报

千问 | 2021-1-27 05:07:22 | 显示全部楼层
r(n,s,k,m)=sum(r(n-1,j,k,m-j)),1≤j≤K,j≤m-j≤k
getResult(n,k,m)=r(n,1,k,m)
回复

使用道具 举报

千问 | 2021-1-27 05:07:22 | 显示全部楼层
最笨的做法,遍历整个数组,求出合适的解。
为了防止重复解答,约定整个数组按照非递减方式排列
//从当前的数组跳转到下一个数组
booleanincreaseArray(intary_value[],intary_size,intary_max_value){
for(inti=ary_size-1;i>=0;i--){
if(ary_value
至于基于纯数学角度的计算公式,还需要思考
回复

使用道具 举报

千问 | 2021-1-27 05:07:22 | 显示全部楼层
上面的方法确实比较“笨”,主要是体现在时间复杂度上。
如代码所示,需要执行(K^N)次increaseArray()的动作,而每次increaseArray()动作里面平均需要执行N/2次进位,
以及每次increaseArray()完毕之后需要执行isAvailableArray动作(平均包含N/2次比较),以及getSummary动作(包含N次求和)
则总的时间复杂度为O(N*(K^N))
但是在此方法的基础上,我们其实是可以让它更快一点的。
此前的方法,可以等价为一个N位长度的K进制数的遍历和各个数位求和的问题。
其实,我们可以先找出一个"最小"的符合条件的K进制数a0,然后在此基础上直接找下一个(即第二小的)符合条件的K进制数a1,再在此基础上寻找a2,a3,...一直到找不到下一个进制数为止。
这里的"符合条件"指的是
1)各个数位之和=M
2)各个数位的内容介于min_value(在这里是1)以及max_value(在这里是K)
3)高位数字=M,然后再调整最后一个修改的高位,使得数位和=M。
这里可能有两种例外:N*min_value>M和N*max_valuea,意味着增加值的数位一定相比减少至的数位处于”更高位"。
于是,可以提出如下的算法:
在a=v[0]v[1]v[2]...v[N],
找出最低位的"可以增加值"的数v[x],
然后再在v[x+1]...v[N]中,找出最高位的"可以减少值"的数v[y],
然后v[x]=v[x]+1,v[y]=v[y]-1。
这样构造出来的数自然满足和不变性和最小性原则
然后就是对"可以增加值"的v[x]以及可以减少值的v[y]的界定。
很显然,如果有下标x=2,则意味着v[x]可以加1。
再从d[x]开始向从前往后找第1个d[t],使得d[x]+d[x+1]+...+d[t]>=2,则意味着v[t+1]可以减1。
然后来看一下大概的时间复杂度。为了找到一个解,我们需要经过N/2+N/4次的查找,所以总的时间与总的解的数量有关。
如果有A个解,则总的解复杂度为O(A*N)。
凭直觉,我觉得这种算法应该要快一点。
---------------------------------------------------------------------------
以下是大致代码
//获取第一个解(返回false表示没有找到。)
booleangetFirstAnswer(intary_value[],intary_size,intmin_value,intmax_value,inttarget_value){
for(inti=0;itarget_value){
returnfalse;
}elseif(sum_value==target_value){
returntrue;
}else{
for(inti=ary_size-1;i>=0;i--){
ary_value=max_value;
sum_value=sum_value+max_value-min_value;
if(sum_value>=target_value){
ary_value=ary_value-(sum_value-target_value);
returntrue;
}
}
returnfalse;
}
}
//计算各个元素与前一元素的差
voidcalcDifference(int[]ary_value,intary_size,int[]difference){
for(inti=ary_size-2;i>=0;i--){
difference=ary_value[i+1]-ary_value;
}
}
//查找最小的可以增加的元素编号,返回-1表示没有找到。
intgetMinIncreaseableIndex(int[]difference,intary_size){
intdiff=0;
for(inti=ary_size-2;i>=0;i++){
diff=diff+difference;
if(diff>=2){
returni;
}
}
return-1;
}
//在指定位置之后,查找最大的可以减少数字的位置编号。(-1表示没有找到,在本算法中,不可能出现该状况)
intgetMaxDecreaseableIndex(int[]difference,intary_size,intfirstIndex){
diff=0;
for(inti=firstIndex;i=2){
returndiff+1;
}
}
return-1;
}
//获取下一个"最小的"解,false表示没有找到有效解
booleangetNextAnswer(int[]ary_value,intary_size,int[]difference){
intfirstIndex=getMinIncreaseableIndex(difference,ary_size);
if(firstIndex=0)&&(firstIndex=0)&&(firstIndex-1=0)&&(lastIndex=0)&&(lastIndex-1
心目中的例子大致如下:(min=1,max=6,数字数量=7,目标值20)
FirstAnswer:
1111277
然后依次搜索到以下解
1111367
1111457
1111466
1111556
1112456
1112555
1113455
1114445
1123445
1124444
1133444
1223444
1233344
1333334
2233334
2333333
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题

0

回帖

4882万

积分

论坛元老

Rank: 8Rank: 8

积分
48824836
热门排行