枚举 之子集生成

感觉最近学的特别慢,好笨啊。

数据结构已经学完了。但是图,二叉树学得不好。寒假结合算法好好的扎实下。

进入正题。

7.1介绍的是简单的枚举第一题注意对abcde/fghij=n,n已知,要对被除数进行分析,abcde/fghij=n,被除数和除数均可以有前导零。所以只用分析除数,被除数>98765就是错误的。除数分为两种情况,有前导零,没有前导零。没有前导零直接转换为字符串保存在数组中进行比较,前面需要补零,strcmp函数。说的很乱。第二题最大乘积需要把握住子序列长度,子序列起点。分数拆分主要就是注意转换。

7.2就是递归和枚举,但是我没太熟悉每一步递归过程,就是会用next_permutation(),prev_permutation();这两个函数的用法。

CF的题不错,我做的是洛谷的题。NYOJ最近一直登不上不知道为啥。

子集生成紫书上介绍了三种方法:

1.增量构造法

一次选出一个元素放到集合中,

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int A[100];
void print_subset(int n,int *A,int cur)
{
  for(int i=0;i<cur;i++)//打印当前集合
    printf("%d ",A[i]);
  printf("\n");
  int s=cur?A[cur-1]+1:0;//确定当前元素的最小可能值
  //cur=0时选取0,否则选取A[cur-1]+1.
  for(int i=s;i<n;i++)//定序的方法
  {
    A[cur]=i;//递归构造子集
    print_subset(n,A,cur+1);
  }
}
int main()
{
  int n;
  cin>>n;
  print_subset(n,A,0);
  return 0;
}

运行结果如 3为用到了增序的方法用到定序的方法是为了避免同一个集合出现两次。解答树节点2^n个。

2.位向量法。

构造位向量B[i]的方法

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int A[100];
void print_subset(int n,int *B,int cur)
{
  if(cur==n)
  {
    for(int i=0;i<cur;i++)
      if(B[i])  printf("%d ",i);
    printf("\n");
   return;
  }
  B[cur]=1;//选第cur个元素
   print_subset(n,B,cur+1);
  B[cur]=0;//不选第cur个元素
   print_subset(n,B,cur+1);
}
int main()
{
  int n;
  cin>>n;
  print_subset(n,A,0);
  return 0;
}

解答树节点2^n+1 -1个。注意输出结果和上面的顺序不同。

3.二进制法。

这种方法是最常用的方法。注意运算的相关性质。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
void print_subset(int n,int s)
{
  for(int i=0;i<n;i++)
   if(s&(1<<i)) printf("%d ",i);//1<<i  即为1*2^i
  printf("\n");
}
int main()
{
  int n;
  cin>>n;
  for(int i=0;i<(1<<n);i++)//i*2^n
    print_subset(n,i);
  return 0;
}

输出结果是注意一些基本操作。A&B 表示A,B的交。A|B表示A,B的并。 A^B表示A,B的对称差。推荐使用第三种方法,代码量少,但是可读性不好。

猜你喜欢

转载自blog.csdn.net/cjh1459463496/article/details/86680102
今日推荐