C语言名题精选百则:所有子集,字典子集,Gray子集

C语言名题百则 3.1 列出所有子集 (direct.c)

分析:每一个元素只有两种可能,在子集中和不在子集中。

#include <iostream>
#include <cstdio>
using namespace std;
int p[20],top;
int main()
{
    int n;
    while(cin>>n){
        for(int i=0;i<(1<<n);i++){
            top=0;
            for(int j=0;j<n;j++){
                if(i&(1<<j)) p[top++]=j+1;
            }
            for(int j=0;j<top;j++){
                printf("%3d",p[j]);
            }
            puts("");
        }
    }
    return 0;
}

C语言名题百则 3.2 列出所有子集——字典序列 (lexical.c)

分析:抓住字典序升序的规律。

#include <iostream>
#include <cstdio>
using namespace std;
int p[25],n;
void show(int top){
    for(int i=1;i<=top;i++) printf("%3d",p[i]);
    puts("");
}
int main()
{
    while(cin>>n){
        printf("\n");
        int top=0;
        for(int i=1;i<(1<<n);i++){
            if(p[top]<n){
                p[top+1]=p[top]+1;
                top++;
                show(top);
            }
            else if(p[top]==n){
                p[top-1]++;
                top--;
                show(top);
            }
        }
    }
    return 0;
}

C语言名题百则 3.3 产生Gray码 (Graycode.c)


格雷码的特点:虽然自然二进制码可以直接由数/模转换器转换成模拟信号,但在某些情况,例如从十进制的3转换为4时二进制码的每一位都要变,能使数字电路产生很大的尖峰电流脉冲。而格雷码则没有这一缺点,它在相邻位间转换时,只有一位产生变化。
典型格雷码是一种具有反射特性和循环特性的单步自补码,它的循环、单步特性消除了随机取数时出现重大误差的可能,它的反射、自补特性使得求反非常方便。
二进制转成格雷码是编码较为方便的方式,参考博客:
http://blog.csdn.net/thearcticocean/article/details/48345811

现在又看这种生成格雷码的方法,不禁想问,它是怎样被发现的?

对照自然二进制数和格雷码可以发现,不看自然二进制和格雷码的最高位,它们剩下的部分进行异或运算所得到的结果刚好是二进制前n-1位。例如12对应的1100~1010,100^010=110。那么反推,十进制数字对应的格雷码的最高位和二进制最高位一样,剩下的部分就该是二进制后n-1位和前n-1位进行异或运算得到。即是二进制转成格雷码的方法。【异或运算的特点: if c=a^b  so  a=c^b  and b=a^c 】
异或运算的特点让我想起了那个加密解密的例子:
package pg;

public class Main {
 public static void main(String[] args) {
      char [] f={'皆','大','欢','喜'};
      char sec='*';
      for(int i=0;i<f.length;i++){
       f[i]=(char)(f[i]^sec);
      }
      for(int i=0;i<f.length;i++){
       System.out.printf("%c ", f[i]);
      }
      System.out.println("");
      for(int i=0;i<f.length;i++){
       f[i]=(char)(f[i]^sec);
      }
      for(int i=0;i<f.length;i++){
       System.out.printf("%c ", f[i]);
      }
 }
}
output:
皬 复 欈 営
皆 大 欢 喜

扯远了,回到本问题上来:可以用二进制生成格雷码,然后输出格雷码对应的子集。
#include <cstdio>
#include <iostream>
using namespace std;
int p[25],top;
void show(int x){
 top=0;
 while(x){
  p[top++]=x%2;
  x>>=1;
 }
 for(int i=top-1;i>=0;i--) cout<<p[i];
 cout<<endl;
}
int main()
{
     int n,t;
     while(cin>>n){
      for(int i=0;i<(1<<n);i++){
       t=i^(i>>1);
       //show(t);
       top=0;
       for(int j=0;j<n;j++){
        if(t&(1<<j)){
         p[top++]=j+1;
        }
       }
       for(int j=0;j<top;j++){
        printf("%3d",p[j]);
       }  
       puts("");
      }
     }
     return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值