代码源:113、可重排列

378 阅读2分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路 logo.png

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目描述

这是3月22日代码源div2的每日一题。

知识点:dfs,STL

可重排列 - 题目 - Daimayuan Online Judge

按字典序从小到大,输出所有序列,满足其中有p1个1, p2个2, ……, pn个n。

输入格式

第一行,一个整数n。

第二行,若干个整数p1,p2,…,pn。

输出格式

若干行,每行若干个整数,表示满足条件的序列。

样例输入

3
1 2 2

样例输出

1 2 2 3 3
1 2 3 2 3
1 2 3 3 2
1 3 2 2 3
1 3 2 3 2
1 3 3 2 2
2 1 2 3 3
2 1 3 2 3
2 1 3 3 2
2 2 1 3 3
2 2 3 1 3
2 2 3 3 1
2 3 1 2 3
2 3 1 3 2
2 3 2 1 3
2 3 2 3 1
2 3 3 1 2
2 3 3 2 1
3 1 2 2 3
3 1 2 3 2
3 1 3 2 2
3 2 1 2 3
3 2 1 3 2
3 2 2 1 3
3 2 2 3 1
3 2 3 1 2
3 2 3 2 1
3 3 1 2 2
3 3 2 1 2
3 3 2 2 1

数据规模

对于100%的数据,保证1≤n≤9,1≤pi≤9,保证总的序列个数不超过10^5个。

两个做法:

1、用dfs枚举每个位置上可能的数字,枚举完最后一位的时候把整个数组输出。

AC代码

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#define endl '\n';
typedef long long ll;
typedef pair<ll, ll>PII;
const int N = 1e6 + 50;
int f[N], n, u;
vector<int> v;

void dfs()
{
    if (v.size() == u)
    {
        for (auto i : v)cout << i << " ";
        cout << endl;
        return;
    }
    for (int i = 1; i <= n; i++)
    {
        if (f[i] != 0)
        {
            f[i]--;
            v.push_back(i);
            dfs();
            v.pop_back();
            f[i]++;
        }
    }
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> f[i];
        u += f[i];
    }
    dfs();
    return 0;
}

2、next_permutation

next_permutation() 会生成一个序列的重排列,它是所有可能的字典序中的下一个排列,默认使用 < 运算符来做这些事情。它的参数为定义序列的迭代器和一个返回布尔值的函数,这个函数在下一个排列大于上一个排列时返回 true,如果上一个排列是序列中最大的,它返回 false,所以会生成字典序最小的排列。

使用样例:(生成1~4的所有可能排列)

 vector<int> range{ 1,2,3,4 };
    do {
        copy(begin(range), end(range), ostream_iterator<int>{cout, " "});
        cout << endl;
    } while (next_permutation(std::begin(range), end(range)));

结果:

1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
3 1 2 4
3 1 4 2
3 2 1 4
3 2 4 1
3 4 1 2
3 4 2 1
4 1 2 3
4 1 3 2
4 2 1 3
4 2 3 1
4 3 1 2
4 3 2 1

只要一开始给出这组序列的最小字典序的序列,我们就可以很方便的得到一串数的全排列结果

AC代码

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#define endl '\n';
typedef long long ll;
typedef pair<ll, ll>PII;
const int N = 1e6 + 50;

int f[10],len,mymap[N];

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int n,x=1;
    cin >> n;
    vector<int>ans;
    for (int i = 1; i <= n; i++)
    {
        cin >> f[i];
        for (int j = 0; j < f[i]; j++)
        {
            ans.push_back(x);
        }
        x++;
    }
    do {
        copy(begin(ans), end(ans), ostream_iterator<int>{cout, " "});
        cout << endl;
    } while (next_permutation(begin(ans), end(ans)));
    return 0;
}