0
点赞
收藏
分享

微信扫一扫

递归与分治问题

晴儿成长记 2022-05-02 阅读 42
算法

1、集合的全排列问题
设集合 R={ r1,r2,r3 … r n},显然全排列的数目为 n!。那么如何显示集合的所有全排列呢?
``可以用递归的方式实现。

void  perm(int list[],int k,int m)
{
     if(k==m) //构成一次全排列
     {
         for(int i=0;i<m;i++)
            cout<<list[i];
     }
     else   //
     {
         for(int j=k;j<=m;j++)
         {
             swap(list[k],list[j]);//交换位置
             perm(list,k+1,m);//进入下一个交换递归
             swap(list[k],list[j]);//恢复现场
         }
     }
}

2、整数划分问题
将正整数n表示为一系列正整数之和,

     n=n1+n2+n3+n4+......+nk   (其中,n1>=n2>=n3>=n4........>=nk>0,k>=1)

正整数n的这种表示成为正整数n的划分。正整数n的不同划分个数成为正整数n的划分数,记作p(n)。

int spilt(int m,int n)//m的n划分个数为spilt(m,n);
{
    if(m==1||n==1) return 1;
    else if(m<=n) return spilt(m,m);
    else if(m==n) return spilt(m,m-1)+1;
    else return spilt(m,n-1)+spilt(m-n,n);//前一个式子没有n,后一个式子是对n的划分
}

3、选择问题
选择问题(selection problem)是求一个n个数列表的第k个最小元素的问题。这个数字被称为第k个顺序统计量(order statistic)。当然,对于k=1或者k=n的情况,我们可以扫描整个列表,找出最小或者最大的元素。对于其他情况,我们可以对列表进行排序,然后返回第k个元素。

select(int left,int right,int k)//初始时left=0,right=n-1
{
    if(left>=right) return a[left];
    int i=left;
    int j=right+1;
    int pivot=a[left];
    while(true)
    {
        
        do(i++)
            while(a[i]<pivot);
        do(j--)
            while(a[j]>pivot);
        if(i>=j)
            break;
        swap(a[i],a[j]);
    }
    if(j-left+1==k) return pivot;
    a[left]=a[j];
    a[j]=pivot;
    if(j-left+1<k)
        return select(j+1,right,k-j+left-1);
    else return select(left,j-1,k);
    
}

4.循环赛日程表
设有n=2k个运动员要进行网球循环赛。现要设计一个满足以下要求的比赛日程表:
(1)每个选手必须与其他n-1个选手各赛一次;
(2)每个选手一天只能赛一次;
(3)循环赛一共进行n-1天。

 #include <stdio.h>
#define MAX 100
int a[MAX][MAX];
void Copy(int tox,int toy,int fromx,int fromy,int r)
{
         for(int i=0;i<r;i++)
                   for(int j=0;j<r;j++)
                            a[tox+i][toy+j]=a[fromx+i][fromy+j];
}
//构造循环赛日程表,选手的数量n=2^k
void Table(int k)
{
         int i,r;
         int n=1<<k;
         //构造正方形表格的第一行数据
         for(i=0;i<n;i++)
                   a[0][i]=i+1;
         //采用分治算法,构造整个循环赛程
         for(r=1;r<n;r<<=1)
         for(i=0;i<n;i+=2*r)
         {
                   Copy(r,r+i,0,i,r);
                   Copy(r,i,0,r+i,r);
         }
          printf("参赛人数为:%d\n(第i行第j列表示和第i个选手在第j天比赛的选手序号)\n",n);
   for(i=0;i<=n-1;i++)
       for(r=0;r<=n-1;r++)
       {
           printf("%d ",a[i][r]);
                if(r==n-1)
                printf("\n");
       }
}

  int main() {
                int i,r,k;
                int n=2^k;
       printf("比赛选手个数为n(n=2^k),请输入参数K(K>0):\n");
                   scanf("%d",&k);
                   if(k!=0)
                   Table(k);
                return 0;
}

5.选择问题
求第k小的整数

int select(int left,int right,int k)
{
    if(left>=right) return a[left];
    int i=left;
    int j=right;
    int pivot=a[left];
    while(true)
    {
          do{
            i++;
          }
        while(a[i]<pivot);

           do{
            j--;
           }
            while(a[j]>pivot);

        if(i>=j)
            break;
        swap(a[i],a[j]);
    }
    if(j-left+1==k) return pivot;
    a[left]=a[j];
    a[j]=pivot;
    if(j-left+1<k)
        return select(j+1,right,k-j+left-1);
    else return select(left,j-1,k);

}

6.半数集问题
一、问题描述:
给定一个自然数n,由n 开始可以依次产生半数集set(n)中的数如下。
(1) n∈set(n);
(2) 在n 的左边加上一个自然数,但该自然数不能超过最近添加的数的一半;
(3) 按此规则进行处理,直到不能再添加自然数为止。
例如,set(6)={6,16,26,126,36,136}。半数集set(6)中有6 个元素。
注意半数集是多重集。
对于给定的自然数n,计算半数集set(n)中的元素个数。

int comp(int n)
{
    int ans=1;
    if(a[n]>0) return a[n];
    for(int i=1;i<n/2;i++)
        ans+=comp(i);
    a[n]=ans;
    return ans;
}

7.最大字段和问题
给定由n个整数组成的序列(a1, a2, …, an),最大子段和问题要求该序列形如
的最大值(1≤i≤j≤n),当序列中所有整数均为负整数时,其最大子段和为0。例如,序列(-20, 11, -4, 13, -5, -2)的
最大子段和为
在这里插入图片描述

int MaxSum(int a[ ], int left, int right)
   {
       sum=0;
       if (left= =right) {      //如果序列长度为1,直接求解
           if (a[left]>0) sum=a[left];
           else sum=0;
       }
      else {
          center=(left+right)/2;    //划分
          leftsum=MaxSum(a, left, center);  
                                                       //对应情况①,递归求解
          rightsum=MaxSum(a, center+1, right);  
                                                       //对应情况②,递归求解
 s1=0; lefts=0;              //以下对应情况③,先求解s1
        for (i=center; i>=left; i--)
        {
            lefts+=a[i];
            if (lefts>s1) s1=lefts;
        }
        s2=0; rights=0;             //再求解s2
        for (j=center+1; j<=right; j++)
        { 
            rights+=a[j];
            if (rights>s2) s2=rights;
        }
        sum=s1+s2;              //计算情况③的最大子段和 
        if (sum<leftsum) sum=leftsum;  
                     //合并,在sum、leftsum和rightsum中取较大者
        if (sum<rightsum) sum=rightsum;
     }
     return sum;
}
举报

相关推荐

0 条评论