next_permutation算法(基于交换)

9 篇文章 0 订阅
5 篇文章 0 订阅

nextpermutation 算法
这个算法如何实现 我们要观察对于任意的123456
对于一个任意的字符串
我们如果要找他的下一个全排列
就应该变动其数字 使得其数值增大 且是最小的增大
那么仅使用以上数字 如何才能使其变大 但变大的数字 是所有变大范围内最小的可能 我们称其为 最小变大


我们看 如果是 1234 下一个数 1243
再来 1243 下一个数是 1342
那么 1342 下一个数是 1423 > 1432
1432 > 2134

像4,43,432这种时候
就需要 把后面的一个数字和前面的一个数字更换
从而实现到最小变大 那么进一步地
我们发现 每次这种变化都是一个递减序列 (如 4,43,432)
把其中最小的数字 和4前面的数字交换 得到一个新序列
因为只有这样才能保证 我们得到新序列才是向“最小变大”

我们需要在后面的递减序列中找到一个比递减序列第一个元素前的
那个元素 我们姑且叫他第0号元素 我们找到一个比0号元素大的
且是最接近0号元素的数字 和 0号交换 才能实现“最小变大“

好了 那么 我们就有代码的思路了
我们发现 任何数串 都可以看成一个后面拖着一个递减序列的数串
无论是什么数字 后面总有一个递减序列 有的是一个元素,有的是多个元素

如何找到他下一个排列?我们要做的是
把递减序列前的第0元素 和递减序列中的比0号元素大且最接近的元素
他俩交换 这样才能实现变大 而且可以推得 递减序列中一定存在元素比第0号元素大

为什么呢? 因为若是不这样 0号元素就在非严格
递减序列里了 所以一定递减序列中有元素比他大
那么 交换完成后就完成任务了吗? 还不够
比如 1342  > 1432 >1423
        1      2     3

我们发现 还需要调整一下才能得到交换后的数串最小
如何调整? 就是要把交换后原来的递减序列从小到大排序才能使其最小
所以我们这里就知道了 由于交换后的0号元素新位置 本来就比0号大
所以仍旧是递减序列

那么就总结为这几个步骤

1 找出递减序列前的第0元素
2 找出递减序列中大于第0元素的最接近元素
3 交换两个元素
4 将0号元素后的序列从小到大排序

我们就得到了他的下一排列

 

模板例题:POJ-3785

题意:就是求下一个排列

 

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
char a[100];
bool Next(int s,int e){
        int l,j = e-1;
        while(j>0&&a[j]>=a[j+1])j--; //1

        if(j==0)return 0;//如果完全逆序 则不必交换 已经是最大可能了

        for(l = e;a[l]<=a[j];l--);//2

        swap(a[l],a[j]);//3

        reverse(a+j+1,a+e+1);//4
        return 1;
}
int main(){
    int n;
    scanf("%d",&n);
    while(n--){
        int num;
        scanf("%d %s",&num,a+1);

        int len = strlen(a+1);
        if(Next(1,len))printf("%d %s\n",num,a+1);
        else printf("%d BIGGEST\n",num);
    }

    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值