排列问题
时间限制: 1S类别: 搜索->中等
问题描述 :
求一个0~N-1的排列(即每个数只能出现一次),给出限制条件(一张N*N的表,第i行第j列的1或0,表示为j这个数能不能出现在i这个数后面,并保证表中的第i行第i列为0,i和j都从0开始),将这个排列看成一个自然数,求从小到大排序第K个排列。
样例输入
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
输入说明 :
第一行为N和K,接下来的N行,每行N个数,0表示不能,1表示能
N<=10,K<=500000
输出说明 :
所求的排列
输入范例 :
3 2
0 1 1
1 0 0
0 1 0
输出范例 :
1 0 2
思路:
先对限制条件进行转换 写出一个筛选已有排列是否满足条件的函数
然后dfs得到全排列,每得到一个排列可以筛一次,计数达到k时,对其输出
代码:
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<iomanip>
#include<vector>
#include<string>
#include<set>
#include<utility>
using namespace std;
int n, k, m = 0;
bool vis[15];
int path[15];
int condition[15][2];
//筛选函数
bool check(int path[], int condition[][2]) {//condition[][1]不能在condition[][0]后面,否则fasle
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < m; j++) {
if (path[i] == condition[j][0] && path[i + 1] == condition[j][1])
return false;
}
}
return true;
}
void dfs(int u, int& k) {
if (u == n) {//dfs到底,得到一个排列序列,判断该序列是否符合条件
if (check(path, condition)) {
k--;
if (k == 0) {
for (int i = 0; i < n; i++) {
cout << path[i];
if (i != n - 1)
cout << ' ';
}
cout << endl;
}
}
return;
}
//继续dfs加入元素得到排列序列
for (int i = 0; i < n; i++) {
if(!vis[i]) {
path[u] = i;
vis[i] = true;
dfs(u + 1, k);
vis[i] = false;
}
}
}
int main() {
cin >> n >> k;
int limit[15][15];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> limit[i][j];
if (limit[i][j] == 0 && i != j) {//存储筛选条件
condition[m][0] = i;
condition[m][1] = j;
m++;
}
}
}
dfs(0, k);
return 0;
}
运行结果截图