一.背景
很多在学c++的人往往容易混淆常规的排列与组合,但其实在常规排列组合之外的还有一种类型——不限个数的排列,那就会有人问了,为什么没有不限个数的组合呢?具体我们往下看。
二.从结果角度分析
从左到右的结果我们可以理解为
往3个相同桶中,放2个不同的球的所有情况(每个桶至少一个)
往3个相同桶中,放2个相同的球的所有情况(每个桶至少一个)
往3个相同桶中,放2个不同的球的所有情况
数学上我们这样表达:
从这次的结果类比上面的结果我们不难想到,排列与组合是没有意义的。但是不限个数的排列却是可以的,即种情况。
三.从代码角度分析
排列:
#include <bits/stdc++.h>
using namespace std;
const int M=1e5+10;
int n,m,a[M],ans;
bool vis[M];
void dfs(int dep,int pre){
if(dep==m+1){
for(int i=1;i<=m;i++) printf("%d ",a[i]);
printf("\n");
ans++;
return;
}
for(int i=1;i<=n;i++){
if(vis[i]) continue;
vis[i]=1;
a[dep]=i;
dfs(dep+1,i);
vis[i]=0;
}
return;
}
int main(){
scanf("%d%d",&n,&m);
cout<<endl;
dfs(1,0);
cout<<ans;
return 0;
}
组合:
#include <bits/stdc++.h>
using namespace std;
const int M=1e5+10;
int n,m,a[M],ans;
bool vis[M];
void dfs(int dep,int pre){
if(dep==m+1){
for(int i=1;i<=m;i++) printf("%d ",a[i]);
printf("\n");
ans++;
return;
}
for(int i=pre+1;i<=n;i++){
a[dep]=i;
dfs(dep+1,i);
}
}
int main(){
scanf("%d%d",&n,&m);
cout<<endl;
dfs(1,0);
cout<<ans;
return 0;
}
不限个数的排列:
#include <bits/stdc++.h>
using namespace std;
const int M=1e5+10;
int n,m,a[M],ans;
bool vis[M];
void dfs(int dep,int pre){
if(dep==m+1){
for(int i=1;i<=m;i++) printf("%d ",a[i]);
printf("\n");
ans++;
return;
}
for(int i=1;i<=n;i++){
a[dep]=i;
dfs(dep+1,i);
}
}
int main(){
scanf("%d%d",&n,&m);
cout<<endl;
dfs(1,0);
cout<<ans;
return 0;
}
不难发现,三个的区别无非就在于dfs中for循环的起始值与后续的标记数组
起始值为1 有标记数组排列
起始值为pre+1 无标记数组组合
起始值为1 无标记数组不限个数的排列
那么回到最开始的问题,是不是有
起始值为pre+1 有标记数组不限个数的组合呢?
尝试过后我们发现结果和组合的结果是一样的。因为标记数组旨在防止重复的编号进入数组,而如果for循环从pre+1开始,永远不会有“回头”的编号,因此标记数组就无用了。