数的拆分和划分问题都可以用到dfs和DP去写,但是dfs代码比较简洁而且思路比较容易想出来,所以我写这种问题习惯上去用dfs去写。
两道luogu经典例题:
自然数的拆分问题
思路:题目要求按照数字从小到大排序输出,每次深搜的时候起点就赋值为上次深搜传参过来的数即可。(这题数据很小,打表也可以过
ACcode
#include<bits/stdc++.h>
using namespace std;
int n,a[10];
void print(int t)
{
for(int i=0;i<t;i++)
{
if(i==0)cout<<a[i];
else cout<<"+"<<a[i];
}
cout<<endl;
}
void dfs(int x,int y,int z)
//x为从x开始选,y为所选数字之和,z为选第z个数
{
if(x==n)return;
if(y==n)
{
print(z);
return;
}
for(int i=x;i<=n-y;i++)
{
a[z]=i;
dfs(i,y+i,z+1);
}
}
int main()
{
cin>>n;
dfs(1,0,0);
return 0;
}
数的划分
因为一个数拆分成k个数不同的顺序只能算是一种答案,所以这个题本质上和上面自然数的拆分问题差不多,只是这个题限制的拆的份数。
ACcode
#include<bits/stdc++.h>
using namespace std;
int n,k,a[10];
int ans;
void dfs(int x,int y,int z)//x为从x开始,y为数字之和,z为选择的第z个数
{
if(x==n)return;
if(z==k)
{
if(y==n)ans++;
return;
}
for(int i=x;y+i*(k-z)<=n;i++)
dfs(i,y+i,z+1);
}
int main()
{
cin>>n>>k;
dfs(1,0,0);
cout<<ans;
return 0;
}