子集生成算法

子集生成算法:给定一个集合,枚举所有可能的子集。为了简单起见,先讨论的集合中没有重复元素。

增量构造法

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:从代码量看,枚举子集的最简单方法是二进制法。

猜你喜欢

转载自blog.csdn.net/k42946/article/details/81636651