问题描述
求一个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
参考自https://blog.csdn.net/weixin_44778155/article/details/104672498
这道题目题意比较难理解,简单解释一下。
首先我们生成一个0~n-1的全排列,比如n=3的时候,全排列按顺序为:
第一:0 1 2
第二:0 2 1
第三:1 0 2
第四:1 2 0
第五:2 0 1
第六:2 1 0
然后我们给出了限制,这个限制是通过一个n*n的矩阵给出的,我们开一个二维数组vis[][]记录这个矩阵。
在这个矩阵里,从左上往右下i=j这条对角线上的0没有用,我们不用看。
我们只看其他位置有没有0,在样例里也就是vis[1][2]=0,vis[2][0]=0
就是说2不能紧贴着跟在1的后面,中间隔了几个数然后2在1后面是可以的。所以上面六个全排列中的第一,第四不符合要求,删去。
然后0不能紧贴着跟在2后面。上面的第五删去。
所以剩下的全排列是:
第一:0 2 1
第二:1 0 2
第三:2 1 0
然后样例k=2,所以输出第二个全排列1 0 2
然后这段代码就好理解了。
1 #include <bits/stdc++.h> 2 using namespace std; 3 int n, k, cnt; 4 int a[20]; 5 int vis[20][20]; 6 bool check() { //check函数用于判断当前这个全排列是否符合题意 7 for (int i = 1; i < n; i++) { //遍历这个全排列,看看是否存在某个数不能紧贴在某个数后面的情况 8 if (vis[a[i - 1]][a[i]] == 0) { 9 return false; 10 } 11 } 12 return true; 13 } 14 int main() { 15 cin >> n >> k; 16 for (int i = 0; i < n; i++) { 17 a[i] = i; //生成0~n-1的序列 18 } 19 for (int i = 0; i < n; i++) { //输入限制 20 for (int j = 0; j < n; j++) { 21 cin >> vis[i][j]; 22 } 23 } 24 do { 25 if (check()) { //判断这个序列是否符合要求 26 if (++cnt == k) { //若符合要求,++cnt。求到第k个符合条件的全排列的话,break 27 break; 28 } 29 } 30 } while (next_permutation(a, a + n)); 31 for (int i = 0; i < n; i++) { //输出这个全排列 32 cout << a[i] << " "; 33 } 34 cout << endl; 35 return 0; 36 }