《算法竞赛·快冲300题》每日一题:“错位排列”

算法竞赛·快冲300题》将于2024年出版,是《算法竞赛》的辅助练习册。
所有题目放在自建的OJ New Online Judge。
用C/C++、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。

文章目录

  • 题目描述
  • 题解
  • C++代码1
  • C++代码2
  • Java代码1
  • Java代码2
  • Python代码1
  • Python代码

错位排列” ,链接: http://oj.ecustacm.cn/problem.php?id=1770

题目描述

【题目描述】 对于一个长度为n的排列a而言,如果不存在a[i]=i,则称之为一个错位排列。按照字典序输出前k个错位排列。
【输入格式】 输入包含两个正整数n和k。输入保证n不超过1000,n*k不超过100000。
【输出格式】 输出k行,每行n个整数表示一个排列。
【输入样例】

样例12 1

样例23 2

【输出样例】

样例12 1

样例22 3 1
3 1 2


题解

   在做本题之前,先回顾DFS的经典应用:按字典序输出n个数的全排列。
   下面的DFS代码能从小到大打印排列。前提是数组a[20]中的数字是从小到大的,如果不是,先排序。用b[]记录一个新的全排列,第一次进入dfs()时,b[0]在n个数中选一个;第二次进入dfs()时,b[1]在剩下的n-1个数中选一个;…;直到选了n个数为止,就得到了一个全排列。用vis[]记录某个数是否已经被选过,选用过的数不能在后面继续选。
   代码输出:“1 2 3 ; 1 3 2 ; 2 1 3 ; 2 3 1 ; 3 1 2 ; 3 2 1 ;”

C++代码1

#include
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<<";  ";
       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;
}

【重点】 用DFS按字典序输出全排列。

C++代码2

   回到本题,要求输出错位排列,只要对上面代码略作修改,跳过a[i]=i的排列即可。

#include
using namespace std;
const int N = 1010;
int n, k;
int b[N];
bool vis[N];
int num=0;                          //统计已经输出的全排列数量
void dfs(int s){
    if(s == n) {
        for(int i=0;i<n;i++)  printf("%d ",b[i]);
        printf("\n");
        num++;                      //输出了一个排列,num数量加1
        return;
    }
    if(num == k) return;            //只输出前k个
    for(int i=1;i<=n;i++)  {
        if(i == s+1) continue;      //处理错位:跳过a[i]=i的情况
        if(!vis[i]){
            vis[i] = true;
            b[s] = i;
            dfs(s+1);
            vis[i] = false;
        }
    }
}
int main(){
    cin >> n >> k;
    dfs(0);        //前n个数的全排列
    return 0;
}

Java代码1

输出全排列的代码

import java.util.*;
public class Main {
    static int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
    static boolean[] vis = new boolean[20];
    static int[] b = new int[20];
    public static void dfs(int s, int t) {
        if (s == t) {
            for (int i = 0; i < t; ++i)   System.out.print(b[i] + " ");            
            System.out.print(";  ");
            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;
            }
        }
    }
    public static void main(String[] args) {
        int n = 3;
        dfs(0, n);
    }
}

Java代码2

本题的代码

import java.util.*;
public class Main {
    static int N = 1010;
    static int n, k;
    static int[] b = new int[N];
    static boolean[] vis = new boolean[N];
    static int num = 0;            // 统计已经输出的全排列数量

    public static void dfs(int s) {
        if (s == n) {
            for (int i = 0; i < n; i++) System.out.print(b[i] + " ");
            System.out.println();
            num++;         // 输出了一个排列,数量加1
            return;
        }
        if (num == k) return;         // 只输出前k个
        for (int i = 1; i <= n; i++) {
            if (i == s+1) continue;   // 处理错位:跳过a[i]=i不输出
            if (!vis[i]) {
                vis[i] = true;
                b[s] = i;
                dfs(s+1);
                vis[i] = false;
            }
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        k = sc.nextInt();
        dfs(0);  // 前n个数的全排列
    }
} 

Python代码1

输出全排列的代码

a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
vis = [False] * 20
b = [0] * 20
def dfs(s, t):
    if s == t:
        for i in range(t):   print(b[i], end=" ")
        print(";  ", end="")
        return
    for i in range(t):
        if not vis[i]:
            vis[i] = True
            b[s] = a[i]
            dfs(s + 1, t)
            vis[i] = False
n = 4
dfs(0, n)

Python代码

本题的代码

N = 1010
n, k = map(int, input().split())
b = [0] * N
vis = [False] * N
num = 0  # 统计已经输出的全排列数量
def dfs(s):
    global num
    if s == n:
        print(*b[:n])
        num += 1  # 输出了一个排列,数量加1
        return
    if num == k:  return  # 只输出前k个
    for i in range(1, n + 1):
        if i == s + 1:   continue  # 处理错位:跳过a[i]=i不输出
        if not vis[i]:
            vis[i] = True
            b[s] = i
            dfs(s+1)
            vis[i] = False
dfs(0)  # 前n个数的全排列

你可能感兴趣的:(算法竞赛快冲300题,搜索专题,深度优先)