ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

多重全排列的生成与构造算法

2021-12-18 21:32:57  阅读:95  来源: 互联网

标签:多重 排列 temp ++ back int 算法 stack


设有a1+a2+—+aK=N,a1,a2,—,aK为正整数(K>=2),将a[1],a[2],—,a[K]K个数排列至1,2,—N这N个排列位置上,使得a[1],a[2],—,a[K]所占据的排列位置数恰好分别为a1,a2,—,aK,这样占据1,2,—NN个排列位置的a[1],a[2],—,a[K]构成的排列为一个排列位置数为N,排列数数目为K的多重全排列。

现在介绍算法,该算法能够生成符合上述要求的所有多重全排列

说明:以下二种算法仅适用于k>=2的情形,k=1为特殊情形我没有专门处理,不处理问题也不大

算法一(K=3,N=5,a1=2,a2=1,a3=2,a[i]=i):思路简单代码易于理解,代码简单。基本思路为在栈中回溯和向前试探,向前试探寻找满足容量上限的下一个候选数,从而扩大候选解的规模,候选解规模达到N即得到一个多重全排列。无法发现符合容量上限的下一个候选数即回溯

C++代码:

#include "stdafx.h"
#include <vector>
#include <iostream>
using namespace std;

const int K = 3;
const int N = 5;

int search(int start, const vector<int> &num, const vector<int> &count)  //从start开始顺序查找满足容量限制的第一个数字,查找失败返回0,否则返回找到的数字
{
    for (; start <= K; ++start)
    {
        if (num[start - 1] + 1 <= count[start - 1])
            return start;
    }
    return 0;
}
int main()
{
    vector<int> count{ 2, 1, 2 };  //a1,---,ak值
    vector<int> num(count.size(), 0); //统计循环过程中1,---,k的数目
    vector<int> arrange(N, 0);  //存放找到的多重全排列的栈结构
    int i = 1;   //栈顶指针
    int number = 0;
    bool TF = true;  //回溯标志

    while (true)
    {
        if (i == 1)
        {
            if (TF == true)
            {
                arrange[i - 1] = 1;
                ++num[0];
                ++i;
            }
            else
            {   
                --num[arrange[i - 1] - 1];
                ++arrange[i - 1];
                if (arrange[i-1] > K)
                {
                    break;
                }
                else
                {
                    ++num[arrange[i - 1] - 1];
                    ++i;
                    TF = true;
                }
            }
        }
        else
        {
            if (TF == true)
            {
                arrange[i - 1] = search(1, num, count);
                ++num[arrange[i - 1] - 1];
            }
            else
            {
                int temp;
                if ((temp = search(arrange[i - 1] + 1, num, count)) == 0)
                {
                    --num[arrange[i - 1] - 1];
                    --i;
                    continue;
                }
                else
                {
                    --num[arrange[i - 1] - 1];
                    arrange[i - 1] = temp;
                    ++num[arrange[i - 1] - 1];
                }
            }

            if (i == N)   //找到一个多重全排列输出
            {
                ++number;
                cout << "第" << number << "个多重全排列:";
                for (const int &m : arrange)
                {
                    cout << m;
                }
                cout << endl;
                cout << endl;
                TF = false;
            }
            else
            {
                ++i;
                TF = true;
            }
        }
    }
    return 0;
}

运行结果:

 

算法二(K=3,N=5,a1=2,a2=1,a3=2,a[i]=i):代码相对而言更复杂,方法较笨,就是单纯地模拟手工寻找多重全排列的过程,该过程中按a[1],—,a[k]的顺序逐一选择子排列,成功找到子排列则向前试探扩大候选解的规模寻找下一个,当a[1],—,a[k]的所有子排列全部找到后即获得一个多重全排列,当已无新的子排列可供选择时即回溯

C++代码:

#include "stdafx.h"
#include <vector>
#include <iostream>
using namespace std;

bool find(bool TF, int n, int k, vector<int> &temp)  //TF==true,函数从头寻找满足1<=a1<--<ak<=n的第一个排列a1,a2,--,ak存放在temp中
{                                                    //TF==false,函数寻找上一次找到的存放在temp中满足1<=a1<--<ak<=n的排列a1,a2,--,ak的下一个排列,找到存放在temp中返回true,找不到返回false
    int i;

    if (TF == true)
    {
        i = 0;
    }
    else
    {
        i = k - 1;
    }

    while (1)
    {
        if (i == 0)
        {
            if (TF == true)
            {
                temp[i] = 1;
            }
            else
            {
                temp[i]++;
            }

            if (temp[i] > n - k + 1)
                return false;

            if (k == 1)
                return true;
            i++;
            TF = true;
        }
        else
        {
            if (TF == true)
                temp[i] = temp[i - 1] + 1;
            else
            {
                temp[i]++;
                if ((temp[i] - temp[i - 1])>(n - temp[i - 1]) - (k - i) + 1)
                {
                    i--;
                    TF = false;
                    continue;
                }
            }

            if (i == k - 1)
            {
                return true;
            }
            i++;
            TF = true;
        }
    }
}

const int K = 3;
const int N = 5;

struct back
{
    vector<int> sub; //存放find函数找到的多重全排列中的一个子排列的排列位置
    vector<int> father;   //存放多重全排列所有N个排列位置被与其对应的sub子排列之前的所有排列占据后剩下的排列位置
    back(int k) :sub(k, 0) {}
};

void diffset(vector<back> &stack, const vector<int> &count)  //计算father和sub的差集,得到sub的下一排列可取得排列位置
{
    int i = 0;
    int j = 0;
    back temp(count[stack.size()]);
    while (i < stack.back().father.size() && j < stack.back().sub.size())
    {
        if (i + 1 < stack.back().sub[j])
        {
            temp.father.push_back(stack.back().father[i]);
            ++i;
        }
        else if (i + 1 > stack.back().sub[j])
        {
            ++j;
        }
        else
        {
            ++i;
            ++j;
        }
    }

    while (i < stack.back().father.size())
    {
        temp.father.push_back(stack.back().father[i]);
        ++i;
    }

    stack.push_back(temp);

}

int main()
{
    vector<int> count{2, 1, 2};  //a1,---,ak值
    int i = 1;  //循环当前正在处理的子排列序号
    int num = 0;  //多重全排列计数
    bool TF = true;   //回溯标志,true前进至本层,false回溯至本层
    vector<back> stack;
    while (true)
    {
        if (i == 1)
        {
            if (TF == true)
            {
                stack.push_back(back(count[0]));
                find(true, N, count[i - 1], stack.back().sub);

                for (int j = 1; j <= N; j++)
                    stack.back().father.push_back(j);
                ++i;
            }
            else
            {
                if (find(false, N, count[i-1], stack.back().sub) == true)
                {
                    ++i;
                    TF = true;
                }
                else
                {
                    break;
                }
            }
        }
        else
        {
            if (TF == true)
            {
                diffset(stack, count);
                find(true, stack.back().father.size(), count[i - 1], stack.back().sub);
            }
            else
            {
                if (find(false, stack.back().father.size(), count[i - 1], stack.back().sub) == false)
                {
                    stack.pop_back();
                    --i;
                    continue;
                }
            }

            if (i == K)   //找到一个多重全排列,输出
            {
                ++num;
                vector<int> temp(N, 0);
                for (vector<back>::size_type i = 0; i < stack.size(); ++i)
                {
                    for (vector<int>::size_type j = 0; j < stack[i].sub.size(); ++j)
                    {
                        temp[stack[i].father[stack[i].sub[j] - 1] - 1] = i + 1;
                    }
                }

                cout << "第" << num << "个多重排列:";
                for (const int &m : temp)
                {
                    cout << m;
                }
                cout << endl;
                cout << endl;
                TF = false;
            }
            else
            {
                ++i;
                TF = true;
            }
        }
    }

    return 0;
}

运行结果:

标签:多重,排列,temp,++,back,int,算法,stack
来源: https://blog.csdn.net/naturerun/article/details/122017253

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有