第七章:暴力求解法。第一部分

本文介绍了枚举和排列的基本算法技巧,包括简单枚举、枚举排列等,并通过实例详细解析了如何生成1~n的所有排列及可重集的排列,提供了具体的代码实现。

简单枚举:

  一般来说枚举变量比较多的时候,可以查看一下多个枚举变量之间是否有一定的关系,如果有,可以通过枚举其中一个,再依据条件计算其他

枚举变量来减少总的枚举量。

  7.1.1 除法、

  7.1.2 最大乘积、

  7.1.3 分数拆分、

  7.1.4 双基回文数。

 

枚举排列:  

  7.2.1 生成1~n的所有排列

问题描述:

  输入整数n,按照字典序从小到大的顺序输出前n个数的所有排列。

解答:

  依据思路:先输出1,之后是2~9的全排列,再输出2,之后是1,3~9的全排列...等

  可以看出来这里设计递归函数解答比较方便。

  我们设计的递归函数需要以下的参数:

  (1)已经确定的前缀序列,以便于输出。

  (2)需要进行全排列的元素集合,以便依此选择下一位元素。

代码:    

/*
按照字典序,依次输出1~n的排列。
*/
#include <stdio.h>
#include <string.h>
#define N 3
/* 这是个错误范例,因为在递归调用时传递参数为数组地址,第一组调用到底部的时候,visit会被全部赋值,而且f也已满,无法回复到之前的状态;
void Print(int n,char f[],char visit[])
{
    //n代表需要加入f中第n个数字,   f是已经选择的数;visit是用来选择下一个字符的数组。(visit[i]=='0' 表示i还没被选过。)
    if(n==N) {f[N]='\0';printf("%s\n",f);}
    else 
    {
        //char *a=new char[N+1];char *b=new char[N+1];
        for(int i=1;i<N+1;i++)
        {
            if(visit[i]=='0')//表明还没被选择过;
            {
                visit[i]='1';
                f[n]=i+'0';
                Print(n+1,f,visit);
            }
        }//delete f;delete visit;
    }
}
*/

//以下使用另外一个标志变量cur来代替visit来完成其“工作”;
void Print(int n,int *f,int cur)
{
    //n为最大9  f为当前“前缀”序列,用来输出;cur为标志变量,表明现需要在f[cur]中填写数字;
    if(n==cur) {for(int i=0;i<9;i++) printf("%d",f[i]); printf("\n");}
    else
    {
        for(int i=1;i<=9;i++)
        {
            int ok=1;
            for(int j=0;j<cur;j++)
                if(f[j]==i) ok=0;
            if(ok)
                {f[cur]=i;Print(n,f,cur+1);}
        }
    }
}
int main()
{
    int f[10];
    memset(f,0,sizeof(f));
    Print(9,f,0);
    return 0;
}

 

 

7.2.2 生成可重集的排列

问题描述:

  与7.2.1类似,这里是输出数组A中各元素的所有全排列;

 注意点:

  1.可以重复

  2.防止重复现象。

(两者在代码中的改动之处标明了)

代码:

 

#include <stdio.h>
#include <string.h>
#define N 10

int P[N];
void Print(int n,int *P,int *A,int cur)
{
    if(n==cur) {for(int i=0;i<n;i++)printf("%d",A[i]);printf("\n");}
    else
    {
        for(int i=0;i<n;i++)
        {
            if(!i || P[i]!=P[i-1])//防止重复
            {
                int a=0,b=0;
                for(int j=0;j<cur;j++)    if(A[j]==P[i]) a++;//可以重复
                for(int j=0;j<n;j++)    if(P[j]==P[i]) b++;
                if(a<b)
                {
                    A[cur]=P[i];
                    Print(n,P,A,cur+1);
                }
            }
        }
    }
}
int main()
{
    int A[N],i,n;
    printf("请输入数字n:代表数组P中有多少个数字:\n");
    while(scanf("%d",&n)!=EOF)
    {
        for(i=0;i<n;i++)
            scanf("%d",&P[i]);
        memset(A,0,sizeof(A));
        Print(n,P,A,0);
    }
    return 0;
}

 

 

 

 

转载于:https://www.cnblogs.com/songacm/p/3387524.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值