11.4 排列和组合的产生(有重集元素) 对不起大家WWW我死了一段时间

本文介绍了如何实现不重复的全排列和全组合算法,通过统计不同数的个数减少循环次数,避免重复输出。这些算法在搜索问题中常作为基础框架,并能应用于剪枝优化。同时,文章探讨了这两种技巧在信息技术领域的实际应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(1) 全排列
输入 n 个数,输出由这 n 个数构成的排列,不允许出现重复的项(但 n 个数本身可以重复)。
实际上,重复排列的产生是由于同一个位置被多次填入了相同的数,并且这多次填入又是在同一次循环过
程中完成的。所以,可以通过统计每个本质不同的数的个数(输入时注意剔除重复数值),使得循环长度由 n
变为 m,这样,i 一旦自增,就再也不会指向原先填入过的数了。

#include <iostream>
using namespace std;
const int N=10;
int n, m; // 共有n个数,其中互不相同的有m个
int rcd[N]; // 记录每个位置填的数
int used[N]; // 标记m个数可以使用的次数
int num[N]; // 存放输入中互不相同的m个数
void unrepeat_permutation(int l)
{ 
int i; 
if (l == n) // 填完了n个数,则输出
{ 
for (i=0; i<n; i++)
cout<<rcd[i]<<" "; 
cout<<endl;
return; 
} 
for (i=0; i<m; i++) // 枚举m个本质不同的数
if (used[i] > 0) // 若数num[i]还没被用完,则可使用次数减1
{ 
used[i]--; rcd[l] = num[i]; // 在l位置放上该数
unrepeat_permutation(l+1); // 填下一个位置
used[i]++; // 可使用次数恢复
} 
} 
int main()
{ 
int i, j, val; 
cin>>n; 
m = 0; 
for (i=0; i<n; i++)
{ 
cin>>val;
for (j=0; j<m; j++) 
if (num[j] == val)
{
used[j]++; break; 
} 
if (j == m)
num[m] = val, used[m++] = 1; 
} 
unrepeat_permutation(0);
return 0; 
}

(2) 全组合
输入 n 个数,求这 n 个数构成的集合的所有子集,不允许输出重复的项(但 n 个数本身可以重复)。
需要注意的是递归调用时,第二个参数是 i,不再是全组合中的 i+1!

#include <iostream>
using namespace std;
const int N=10;
int n, m; // 输入n个数,其中本质不同的有m个
int rcd[N]; // 记录每个位置填的数
int used[N]; // 标记m个数可以使用的次数
int num[N]; // 存放输入中本质不同的m 个数
void unrepeat_combination(int l, int p)
{ 
int i; 
for (i=0; i<l; i++) // 每次都输出
cout<<rcd[i]<<" ";
cout<<endl; 
for (i=p; i<m; i++) // 循环依旧从p开始,枚举剩下的本质不同的数
if (used[i] > 0) // 若还可以用, 则可用次数减1
{
used[i]--; rcd[l] = num[i]; // 在l位置放上该数
unrepeat_combination(l+1, i); // 填下一个位置
used[i]++; // 可用次数恢复
} 
} 
int main()
{ 
int i, j, val; 
cin>>n;
m = 0; 
for (i=0; i<n; i++)
{ 
cin>>val;
for (j=0; j<m; j++) 
if (num[j] == val)
{ 
used[j]++; break; 
} 
if (j == m)
num[m] = val, used[m++] = 1; 
} 
unrepeat_combination(0, 0); 
return 0; 
} 

排列与组合的应用:搜索问题中有一些本质上就是排列组合问题,只不过加上了某些剪枝和限制条件。解
这类题目的基本算法框架常常是类循环排列、全排列、一般组合或全组合。而不重复排列与不重复组合则是两
种非常有效的剪枝技巧。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值