P2404 问题描述
自然数的拆分问题 任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和。现在给你一个自然数n,要求你求出n的拆分成一些数字的和。每个拆分后的序列中的数字从小到大排序。然后你需要输出这些序列,其中字典序小的序列需要优先输出。
输入输出样例
Sample Input
7
Sample Output
1+1+1+1+1+1+1 1+1+1+1+1+2 1+1+1+1+3 1+1+1+2+2 1+1+1+4 1+1+2+3 1+1+5 1+2+2+2 1+2+4 1+3+3 1+6 2+2+3 2+5 3+4
Wrong Answer
#include <bits/stdc++.h>
using namespace std
;
int n
;
vector
<int> track
;
void dfs(int sum
)
{
if(sum
==n
){
cout
<<track
[0];
for(int i
=1;i
<track
.size();i
++) cout
<<"+"<<track
[i
];
cout
<<endl
; return ;
}
for(int i
=1;i
+sum
<=n
;i
++){
track
.push_back(i
);
dfs(sum
+i
);
track
.pop_back();
}
}
int main()
{
cin
>>n
;
dfs(0);
return 0;
}
这个代码的错误之处就在于深搜时每个分支都从1开始,实际上这样写的话会造成排列重复,比如1+1+1+1+1+2 和1+1+1+1+2+1 都会输出。 措施:观察样例我们发现,后面输出的数总是大于等于前面输出的数,因此可以引入前缀,每次都从前缀的值开始循环。 同时,注意循环结束的条件。
改进
#include <bits/stdc++.h>
using namespace std
;
int n
;
vector
<int> track
;
void dfs(int pre
,int sum
)
{
if(sum
==n
){
cout
<<track
[0];
for(int i
=1;i
<track
.size();i
++) cout
<<"+"<<track
[i
];
cout
<<endl
; return ;
}
for(int i
=pre
;i
+sum
<=n
&&i
!=n
;i
++){
track
.push_back(i
);
dfs(i
,sum
+i
);
track
.pop_back();
}
}
int main()
{
cin
>>n
;
dfs(1,0);
return 0;
}
P1157 问题描述
组合的输出 排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素, 请你输出所有组合。我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。 所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,每个元素占三个字符的位置,所有的组合也按字典顺序。
输入输出样例
Sample Input
5 3
Sample Output
1 2 3 1 2 4 1 2 5 1 3 4 1 3 5 1 4 5 2 3 4 2 3 5 2 4 5 3 4 5
思路1
using namespace std
;
int n
,r
;
vector
<int> track
;
void dfs(int dep
)
{
if(dep
==n
+1){
if(track
.size()==r
){
for(int i
=0;i
<r
;i
++) printf("%3d",track
[i
]);
cout
<<endl
;
}
return;
}
else{
track
.push_back(dep
);
dfs(dep
+1);
track
.pop_back();
dfs(dep
+1);
}
}
int main()
{
cin
>>n
>>r
;
dfs(1);
return 0;
}
这是在求子集的基础上,只在筛选条件上选择子集个数为r的子集输出,需要耗费较高的时间。
思路2
#include <bits/stdc++.h>
using namespace std
;
int n
,r
;
vector
<int> track
;
void dfs(int pre
)
{
if(track
.size()==r
){
for(int i
=0;i
<r
;i
++) printf("%3d",track
[i
]);
cout
<<endl
; return ;
}
for(int i
=pre
;i
<=n
;i
++){
track
.push_back(i
);
dfs(i
+1);
track
.pop_back();
}
}
int main()
{
cin
>>n
>>r
;
dfs(1);
return 0;
}
P1157拓展
根据思路2,不仅可以写出组合的输出C(m,n),还可以写出排列的输出A(m,n) 需要一个vis数组判断该数是否已加入track数组
#include <bits/stdc++.h>
using namespace std
;
int n
,r
;
vector
<int> track
;
int vis
[25];
void dfs(int dep
)
{
if(dep
>r
){
for(int i
=0;i
<r
;i
++) printf("%3d",track
[i
]);
cout
<<endl
; return ;
}
for(int i
=1;i
<=n
;i
++){
if(!vis
[i
]){
track
.push_back(i
);
vis
[i
]=1;
dfs(dep
+1);
track
.pop_back();
vis
[i
]=0;
}
}
}
int main()
{
cin
>>n
>>r
;
dfs(1);
return 0;
}