蓝桥杯-排列问题(50分)

本文介绍了一个编程问题:在特定约束条件下寻找第K个0~N-1的排列。使用了全排列的方法并结合限制条件筛选出有效排列,最终输出符合要求的第K个排列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package exec;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Scanner;

/**
问题描述
  求一个0~N-1的排列(即每个数只能出现一次),给出限制条件(一张N*N的表,第i行第j列的1或0,表示为j-1这个数不能出现在i-1这个数后面,并保证第i行第i列为0)。
       将这个排列看成一个自然数,求从小到大排序第K个排列。
数据规模和约定
  N<=10,K<=500000
输入格式
  第一行为N和K,接下来的N行,每行N个数,0表示不能,1表示能
输出格式
  所求的排列
样例输入
3 2
0 1 1
1 0 0
0 1 0
样例输出
1 0 2
解释:
对于N=3的没有任何限制的情况
第一:0 1 2
第二:0 2 1
第三:1 0 2
第四:1 2 0
第五:2 0 1
第六:2 1 0
根据题目所给的限制条件由于2不能出现在1后面,0不能出现在2后面
第一:0 2 1
第二:1 0 2
第三:2 1 0
 * @author Vivinia
 *
 * 2018年2月8日
 */
public class F {
	static int sum=0,temp=0;
	static LinkedList<L> list;         //两个int元素,代表left不能再right前边
	static int k;
	public static void main(String[] args) {
		list=new LinkedList<L>();
		Scanner input=new Scanner(System.in);
		int n=input.nextInt();
		k=input.nextInt();
		int[][] num=new int[n][n];
		int[] arr1=new int[n];
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				num[i][j]=input.nextInt();
		input.close();
		for(int i=0;i<n;i++) {
			for(int j=0;j<n;j++) {
				if(i!=j&&num[i][j]==0) {  //0代表j不可以在i后边
					list.add(new L(i,j));
				}
			}
		}
		for(int i=0;i<n;i++)
			arr1[i]=i;             //0到n-1存入数组
		perm(arr1,0,n);    //数组,起始位置,长度
	}

	private static void perm(int[] arr1, int j, int n) {     //数组全排列
		if (j < n - 1) {  
            int i, t;  
            for (i = j; i < n; i++) {  
                t = arr1[j];  
                arr1[j] = arr1[i];  
                arr1[i] = t;  
                perm(arr1, j + 1, n);  
                t = arr1[j];  
                arr1[j] = arr1[i];  
                arr1[i] = t;  
            }  
        } else {  
            int i;  
            int flag=0;
            for (i = 0; i < n-1; i++)  {
            	for(int t=0;t<list.size();t++) {
            		if(list.get(t).getLeft()==arr1[i]&&list.get(t).getRight()==arr1[i+1]) {  //如果数组全排列中有两个连续的数据和之前的list集合中某两个元素相等
            			flag=1;    //标志位,代表是通过break退出的循环
            			break;    //跳出循环,代表这组排列作废
            		}
            	}
            	if(flag==1)    //如果内层循环是break出来的,这个循环液break出去
            		break;
            }
            if(i==n-1&&flag==0)     //遍历完没有break,这组符合条件,个数加1
            	sum++;
            if(sum==k&&temp==0) {         //temp标志位用来标记sum==k是否输出,因为递归调用如果不设置标志位有可能会再次进入
            	temp=1;
            	for(i=0;i<n;i++)
            		System.out.print(arr1[i]+" ");
            	System.out.println();
            	return;
            }
        }  
    }  
}
class L{
	int left;
	int right;
	
	public L(int left, int right) {
		super();
		this.left = left;
		this.right = right;
	}
	public int getLeft() {
		return left;
	}
	public void setLeft(int left) {
		this.left = left;
	}
	public int getRight() {
		return right;
	}
	public void setRight(int right) {
		this.right = right;
	}
	
}


1.两组数据通过了一组,不太清楚是什么原因,不知道是不是数组排列顺序的事,因为这个题最终顺序和数组全排列顺序有关,这个我一直不是很理解;

2.小知识点很多,比较散,该设置标志位就设置标志位,很有用;

3.关于一个对象是否存在一个集合中,貌似只能遍历出来然后一个一个比较,“集合.contains(对象)”这是不能用的;

### 蓝桥杯倒序排列算法实现 在蓝桥杯竞赛中,涉及排列组合的问题较为常见。其中,“倒序排列”的概念可以理解为按照字典顺序逆向生成排列。以下是关于如何通过 `next_permutation` 函及其变体来实现这一功能的具体说明。 #### 字典序与倒序排列 标准库中的 `std::next_permutation` 是 C++ 中用于生成下一个排列的工具函。它会基于当前数组的状态,找到其字典序下的下一个排列。如果当前排列已经是最大字典序,则返回到最小字典序状态并返回 `false`[^2]。为了实现倒序排列,可以通过以下方式: 1. **初始化输入**:将给定的据按升序排序作为起始点。 2. **反向迭代排列**:利用 `prev_permutation` 或手动调整逻辑,从最大字典序逐步退回到最小字典序。 #### 实现代码示例 下面是一个完整的 C++ 示例程序,展示如何生成所有可能的倒序排列: ```cpp #include <iostream> #include <algorithm> #include <vector> int main() { std::string s; std::cout << "请输入字符集合(无重复字母):" << std::endl; std::cin >> s; // 对字符串进降序排序,得到最大的字典序排列 std::sort(s.begin(), s.end(), std::greater<char>()); int count = 0; // 记录生成的排列量 while (true) { std::cout << ++count << ": " << s << std::endl; // 尝试获取前一个排列 if (!std::prev_permutation(s.begin(), s.end())) break; } return 0; } ``` 上述代码实现了以下功能: - 首先对输入字符串按降序排序,确保初始状态为最大字典序排列- 使用 `std::prev_permutation` 不断生成上一个排列,直至无法继续生成新的排列为止。 #### 关键点解析 1. **字典序定义**:对于任意两个排列 \(A\) 和 \(B\),若存在某个位置 \(i\) 满足 \(A[i] > B[i]\),则认为 \(A\) 的字典序大于 \(B\)[^2]。 2. **边界条件处理**:当排列达到最小字典序时,`std::prev_permutation` 返回 `false`,此时应停止循环。 3. **时间复杂度**:假设输入长度为 \(n\),每次调用 `std::prev_permutation` 的平均时间为 \(O(n)\),因此总的时间复杂度约为 \(O(n! \cdot n)\)。 #### 应用场景扩展 除了简单的倒序排列外,在实际比赛中还可能出现更复杂的约束条件,例如固定某些位置上的字符或者限定特定子串的存在形式。这些情况下通常需要结合回溯法或其他高级技巧完成解题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值