子集生成算法:给定一个集合,枚举所有可能的子集。为了简单起见,先讨论的集合中没有重复元素。
增量构造法
code:
int ans[1000],n;
bool vis[1000];
void dfs(int x,int cs)
{
for (int i = 1;i <= cs ;i++)
cout<<ans[i]<<" ";
cout<<endl;
for (int i = 1;i <= n;i++)
{
if(!vis[i])
{
ans[cs+1] = i;
vis[i] = 1;
dfs(i,cs+1);
vis[i] = 0;
}
}
}
提示:与全排列不同的是,由于长度不确定,所以每次递归都要输出。边界也不用很明显,不能再加了自然就停止了。
位向量法
第二种思路是构造一个位向量B[i],而不是直接构造子集A本身,其中B[i]=1,当且仅当 i 在子集A中。递归实现如下:
code:
bool b[maxn];
void dfs(int cs)
{
if(cs == n+1)
{
for (int i = 1;i <= n;i++)
if(b[i]) printf("%d ",i);printf("\n");return ;
}
b[cs] = 0;
dfs(cs+1);
b[cs] = 1;
dfs(cs+1);
}
例题:P1460 健康的荷斯坦奶牛 Healthy Holsteins
void dfs(int k,int s)//第k种,取了s个
{
if(k>m)
{
if(check(s))
{
if(s<ans)
{
ans=s;
for(int i=1;i<=s;i++)
dis[i]=dis1[i];
}
}
}
else
{
dis1[s+1]=k;
dfs(k+1,s+1);
dis1[s+1]=0;
dfs(k+1,s);
}
}
二进制法
还可以用二进制来表示{0, 1, 2,…,n-1}的子集S:从右往左第i位(各位从0开始编号)表示元素i是否在集合S中。
void ziji()
{
for (int i = 0 ;i < (1<<n);i++)
{
for (int j = 0;j < n;j++)
if(i&(1<<j))printf("%d ",j+1);printf("\n");
}
}
提示7-8:从代码量看,枚举子集的最简单方法是二进制法。