数学

Author Avatar
Axell 1月 29, 2019
  • 在其它设备中阅读本文章

数学,数论

质数

./archives/24/

约数

./archives/89/

排列

STL:

next_permutation(): next_permutation(a+1,a+n+1); prev_permutation(): prev_permutation(a+1,a+n+1);

实现

一种消重的字符串全排列算法 在字符串全排列中,如果该字符串中存在相同的两个元素,这个字符串的全排列的个数不再是n!,所以要考虑相同元素的情况。下面的实现是利用交换的思想,递归地求解字符串的全排列算法:

void permute(char *str, int i, int n)
{
   int j;

   if (i == n)
     printf("%s\n", str);
   else{
        for (j = i; j <= n; j++){
          if(str[i] == str[j] && j != i)  continue; //为避免生成重复排列,不同位置的字符相同时不交换
          swap((str+i), (str+j));
          permute(str, i+1, n);
          swap((str+i), (str+j));
       }
   }
}
康托展开

展开: 即求这是1~n的第几个排列

#include <bits/stdc++.h>
using namespace std;

int n,ans,a[10];
char s[10];
bool tag[10];
int main(){
    cin>>n;
    scanf("%s",s+1);
    a[1]=1;
    for (int i=2;i<=n;++i) a[i]=a[i-1]*i;  //预处理阶乘
    for (int i=1;i<=n;++i){
        int t=s[i]-'0',tmp=0;
        for (int j=1;j<=n;++j){
            if (j==s[i]-'0') break;
            if (!tag[j]) tmp++; //tmp计算剩余的集合中,s[i]排第几
        }
        tag[t]=1;
        ans+=tmp*a[n-i];
    }
    cout<<ans+1;  //要加上自己
    return 0;
}

逆展开: 即求出1~n的所有排列中,第K个排列是什么

#include <bits/stdc++.h>
using namespace std;

int n,s[10],tag[10],a[10],ans;

int main(){
    cin>>n;
    for (int i=1;i<=n;++i) scanf("%d",&s[i]);
    a[0]=1;
    for (int i=1;i<=n;++i) a[i]=a[i-1]*i;
    for (int i=1;i<=n;++i){
        int t=s[i],tmp=0;
        for (int j=1;j<=n;++j){
            if (j==s[i]) break;
            if (!tag[j]) tmp++;
        }
        tag[t]=1;
        ans+=tmp*a[n-i];
    } //映射为数值
    ans--; //求前一个
    if (ans==-1){ //如果已经是第一个
        cout<<"ERROR"<<endl;
        return 0;
    }
    memset(tag,0,sizeof(tag));
    for (int i=1;i<=n;++i){
        int t=ans/a[n-i];
        ans%=a[n-i];
        for (int j=1;j<=n;++j){
            if (t==0&&!tag[j]){
                t=j;
                break;
            }
            if (!tag[j]) t--;
        }
        tag[t]=1;
        s[i]=t;
    }
    for (int i=1;i<=n;++i) printf("%d ",s[i]); //输出序列
    return 0;
}

另一种做法:

#include <bits/stdc++.h>
using namespace std;

int n,s[10];

int main(){
    cin>>n;
    for (int i=1;i<=n;++i) scanf("%d",&s[i]);
    if (prev_permutation(s+1,s+n+1)) for (int i=1;i<=n;++i) cout<<s[i]<<' ';
    else cout<<"ERROR"<<endl;
    return 0;
}

康托展开适合求间隔较大的两个排列,STL适合求较近的两个排列 康拓展开和逆展开可以用vector优化

知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版本许可协议进行许可。

本文链接:https://hs-blog.axell.top/archives/5/