https://www.luogu.org/problemnew/show/P1025
本题有两种解法 dfs和dp
dfs
dfs很好理解 只是需要加上一个剪枝:
sums+i*(k-t)<=n
其实这个也是很容易理解的 因为我们为了避免重复 将循环从小到大进行 那么如果sums+i*(k-t)>n,说明后面的数肯定不满足条件
#include<bits/stdc++.h> using namespace std; int n,k,anss,sums; void dfs(int now,int t) { if(t==k) { if(sums==n) anss++; return; } for(int i=now;sums+i*(k-t)<=n;i++) { sums+=i; dfs(i,t+1); sums-=i; } } int main() { cin>>n>>k; dfs(1,0); cout<<anss<<endl; }
也可以把sums当作参数传进去:
#include<bits/stdc++.h> using namespace std; int n,k,anss; void dfs(int now,int t,int sums) { if(t==k) { if(sums==n) anss++; return; } for(int i=now;sums+i*(k-t)<=n;i++) dfs(i,t+1,sums+i); } int main() { cin>>n>>k; dfs(1,0,0); cout<<anss<<endl; }
dp
这个不是很好想 我是抄的题解:
f[i][x] 表示 i 分成 x 个非空的数的方案数。
显然 i<x 时 f[i][x]=0 , i=x 时 f[i][x]=1;
其余的状态,我们分情况讨论:
①有1的 ②没有1的
第一种情况,方案数为 f[i-1][x-1]
第二种情况,方案数为 f[i-x][x] (此时 i 必须大于 x)
所以,状态转移方程为: f[i][x]=f[i-1][x-1]+f[i-x][x]
解释一下:
一个数字i被分成K份时,存在两种情况,一个是有一存在,如6分成三份的1 2 3;另一种是没有一存在,比如6分成三份的2 2 2;
如果有1存在,还是用6分三份举例,那么就是对5进行分两份的操作,再加上那个1;如果没有1存在,我们先将6变成(1+a)+(1+b)+(1+c);那么a+b+c=6-3=3;然后他们需要被分配到三个位置上从而防止1的出现——即【i-k】【k】
#include<bits/stdc++.h> using namespace std; long long int n,k,a[205][10],i,j; int main() { cin>>n>>k; for(i=1;i<=n;i++) { a[i][1]=1; a[i][0]=1; } for(i=2;i<=k;i++) { a[1][i]=0; a[0][i]=0; } for(i=2;i<=n;i++) for(j=2;j<=k;j++) { if(i>j) a[i][j]=a[i-1][j-1]+a[i-j][j]; else a[i][j]=a[i-1][j-1]; } cout<<a[n][k]; }