数的划分
解题思路
本题的意思是将一个数n化解为k份,有几种不同的方案。例如k=3的时候,a+b+c=n,求a、b、c有几种不同的取值。为了避免出现重复,搜索的时候按照从大到小搜索。依次枚举a、b、c的值进行判断。
直接搜程序的运行速度是特别慢的,需要控制好搜索的上下界。
约数条件:
(1)由于我们是按照从小到大搜索的,所以扩展节点的下界,不能小于前一个扩展节点的值。
(意思是搜索下个值,一定大于等于当前值)
(2)接下来考虑上界,看图吧
将21分为6份,当搜索到a[i]的时候(也就搜索到5的时候),判断搜索上下界,显然搜索的上界是a[i-1](也就是4, 因为我们是按照递增的顺序进行搜索的)
接下来判断下界,a[i] (a[i]=5)这个数肯定比n(n=21)小,但是用n作为下界,有大的区间完全没用,那怎么才 能比5稍微大一点点呢?那就是求取没被搜索到的数的平均数,如果将已经搜到的数加起来:
设定m=6表示总共要分6份,n = n - a[1] + ... + a[i-1];(表示还有多大的值没被搜索出来,就是5+6的值)
当搜索到4的时候,是搜到了第四个数,k = 4;
后边的平均数就等于n/(m-k+1);
后边的数取平均数,肯定是大于等于a[i]的。
控制好上下界,这道题就解决了。
例题地址:https://loj.ac/problem/10018
AC代码:
#include<bits/stdc++.h> using namespace std; int n,m,sum = 0; int a[10]; void dfs(int k) { if(n == 0) return; if(k == m){ if(n >= a[k])//最后一个数,只要保证大于等于前一个数,就可以 sum++; return; } for(int i = a[k];i <= n/(m-k+1);i++){//设定搜索的上下界,剪枝的一种 a[k+1] = i; n -= i; dfs(k+1); n += i; } } int main(){ scanf("%d%d",&n,&m); a[1] = 1; dfs(1); printf("%d ",sum); return 0; }
我走的很慢,但我从不后退。
——林肯