Full permutation function and self-written permutation

Notice: The new book " Algorithm Competition " I wrote in two years has been handed over to Tsinghua University Press in February 2022 and is expected to be published in July 2022.
"Algorithm Competition" is a " compendium ", covering "basic - intermediate - advanced", about 700 pages in length. Drafts of some knowledge points have been published on this blog.


The Blue   Bridge Cup provincial competition is coming soon , here is a useful basic knowledge point.

  Arrangement is a basic technique commonly used in computer programming, and every algorithm competition has topics that use arrangement.
  Need to master two ways to achieve permutation ( C/C++ group ):
   (1) STL's next_permutation() function. When you need to output all the full permutations, use this function directly.
   (2) Self-written permutation function. If you only need to output a part of the arrangement, you cannot use the next_permutation() function at this time, and you need to write your own code.

1、next_permutation()

The function for finding the " next " full permutation in    STL is next_permutation(). For example, for a sequence of three characters {a, b, c}, next_permutation() can return 6 combinations lexicographically: abc, acb, bac, bca, cab, cba.
   The definition of the function next_permutation() has two forms:

bool next_permutation (BidirectionalIterator first, BidirectionalIterator last);
bool next_permutation (BidirectionalIterator first, BidirectionalIterator last, Compare comp);

   Return value : If there is no next permutation combination, return false, otherwise return true. Each time next_permutation() is executed, the new arrangement will be placed in the original space.
  Note the following:
(1) The range of next_permutation() permutation is [first, last) , including first and excluding last.
(2) next_permutation() starts from the current full permutation and outputs larger full permutations one by one instead of outputting all full permutations. For example, the initial sequence is {b,c,a}, which is not the smallest lexicographically, and only 3 can be output at this time.

#include <bits/stdc++.h>
using namespace std;
int main(){
    
    
    string s="bca";
    do{
    
     
       cout<<s<<endl;
    }while(next_permutation(s.begin(),s.end()));  //end()指向最后一个字符的下一个位置
    return 0;
}    

   The code can only output 3 full permutations , not 6:

bca
cab
cba

(3) If you want to get all the full permutations , you need to start with the smallest full permutation. If the initial full permutation is not the smallest, first use sort() to sort , get the smallest permutation, and then execute next_permutation(). E.g:

#include <bits/stdc++.h>
using namespace std;
int main(){
    
    
    string s="bca"; 
    sort(s.begin(),s.end());  //字符串内部排序,得到最小的排列“abc”
    do{
    
     
       cout<<s<<endl;
    }while(next_permutation(s.begin(),s.end()));
    return 0;
}   

At this point, all the full permutations    can be output , a total of 6:

abc
acb
bac
bca
cab
cba

(4) If there are repeated elements in the sequence, the permutation generated by next_permutation() will be deduplicated. For example "aab", the code outputs 3 permutations {aab, aba, baa}, not 6 permutations.

#include <bits/stdc++.h>
using namespace std;
int main(){
    
    
    string s="aab"; 
    sort(s.begin(),s.end());  //字符串内部排序,得到最小的排列“abc”
    do{
    
     
        cout<<s<<endl;
    }while(next_permutation(s.begin(),s.end()));
    return 0;
}   

   Output 3 permutations:

aab
aba
baa

   There is also a full permutation function prev_permutation() in STL, which seeks the "previous" permutation combination, which is opposite to next_permutation(), that is, "from big to small".
   Although next_permutation() is very convenient, it cannot print the partial permutation of m numbers out of n numbers . In some cases, it needs to be processed during the permutation process. In this case, you must write the permutation function yourself.

2. Self-written permutation function

   The following is a self-written full permutation function, which can achieve partial permutation. Write a full permutation function recursively, and use b[] to record a new full permutation. When entering bfs() for the first time, b[0] selects one of n numbers, and when entering bfs() for the second time, b[1 ] pick one of the remaining n-1 numbers, ..., etc. Use vis[] to record whether a certain number has been selected. The selected number cannot be selected later.
   The code can print all arrangements from small to large, provided that the numbers in a[] are from small to large, you can sort a[] first.

#include<bits/stdc++.h>
using namespace std;
int a[20] = {
    
    1,2,3,4,5,6,7,8,9,10,11,12,13};
bool vis[20];         //记录第i个数是否用过
int b[20];            //生成的一个全排列
void dfs(int s,int t){
    
    
    if(s == t) {
    
          //递归结束,产生一个全排列
        for(int i = 0; i < t; ++i)  
            cout << b[i] << " ";    //输出一个排列            
        cout<<endl;
        return;
    }
    for(int i=0;i<t;i++)
        if(!vis[i]){
    
    
            vis[i] = true;   
            b[s] = a[i];
            dfs(s+1,t);
            vis[i] = false;
        }
}
int main(){
    
    
    int n = 3;
    dfs(0,n);     //前n个数的全排列
    return 0;
}

   output:

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

   If you need to print the arrangement of any m number of n numbers , for example, take the arrangement of any 3 numbers out of 4 numbers, change line 21 to n = 4, and then modify line 7 in dfs() to get the following the code:

#include<bits/stdc++.h>
using namespace std;
int a[20] = {
    
    1,2,3,4,5,6,7,8,9,10,11,12,13};
bool vis[20];        //记录第i个数是否用过
int b[20];           //生成的一个全排列
void dfs(int s,int t){
    
    
    if(s == 3) {
    
         //递归结束,取3个数产生一个排列
       for(int i = 0; i < 3; ++i)     //打印4个数中3个数的排列
            cout << b[i] << " ";
        cout<<endl;
        return;
    }
    for(int i=0;i<t;i++)
        if(!vis[i]){
    
    
            vis[i] = true;
            b[s] = a[i];
            dfs(s+1,t);
            vis[i] = false;
        }
}
int main(){
    
    
    int n = 4;
    dfs(0,n);     //前n个数的全排列
    return 0;
}

output:

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

3. Examples

   The following is an example that requires self-written full permutation and cannot use next_permutation().


Winter vacation homework (Question 6 of C++ Group A in the 2016 Provincial Competition of the Blue Bridge Cup)
Title description: Four operations of addition, subtraction, multiplication and division:
□ + □ = □
□ - □ = □
□ × □ = □
□ ÷ □ = □
each Each square represents a number from 1 to 13, but cannot be repeated.
Q: How many options are there in total?


The title is a 13! full permutation problem. If you use next_permutation(), it is easy to write the following code:

#include <bits/stdc++.h>
using namespace std;
int a[20] = {
    
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
int main() {
    
    
  int ans=0;
  do{
    
     
     if(a[0]+a[1]==a[2] && a[3]-a[4]==a[5] &&a[6]*a[7]==a[8] && a[11]*a[10]==a[9])
          ans++;
  }while(next_permutation(a,a+13));
  cout<<ans<<endl;
}

   Unfortunately, the above code times out seriously, because 13! = 6,227,020,800, running the code, can't end for a long, long time.
   Since next_permutation() must generate a complete permutation every time, and cannot stop in the middle (only generate a part of the full permutation, for example, a permutation of 5 numbers only outputs the first 3 permutations), so in this case it is not easy to use.
   Analysis of the title shows that it is not actually necessary to generate a complete arrangement. For example, if the first 3 numbers of a permutation do not satisfy "□ + □ = □", then the next 9 numbers are wrong no matter how they are arranged. This technique of terminating the search early is called "pruning", and pruning is a common optimization technique in search. The following code adds pruning to the self-written full arrangement.

#include<bits/stdc++.h>
using namespace std;
int a[20]={
    
    1,2,3,4,5,6,7,8,9,10,11,12,13};
bool vis[20]; 
int b[20];    
int ans=0;
void dfs(int s,int t){
    
    
    if(s==12) {
    
    
        if(b[9]*b[10] == b[11])   ans++;  
        return;
    }
    if(s==3 && b[0]+b[1]!=b[2]) return; //剪枝
    if(s==6 && b[3]-b[4]!=b[5]) return; //剪枝
    if(s==9 && b[6]*b[7]!=b[8]) return; //剪枝
    for(int i=0;i<t;i++)
        if(!vis[i]){
    
    
            vis[i]=true;
            b[s]=a[i];  //本题不用a[],改成b[s]=i+1也行
            dfs(s+1,t);
            vis[i]=false;
        }
}
int main(){
    
    
    int n=13;
    dfs(0,n); //前n个数的全排列
    cout<<ans;
    return 0;
}

   Running the code will soon output the answer:

64

4. Exercises

The following exercises for the arrangement are from the official website of the Blue Bridge Cup.
https://www.lanqiao.cn/problems/782/learning
https://www.lanqiao.cn/problems/572/learning
https://www.lanqiao.cn/problems/269/learning
https://www .lanqiao.cn/problems/208/learning

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324126204&siteId=291194637