题意:给定1,2,3...N个数的集合,现在求所有非空子集(相同元素不同位置视为不同)按字典序排序后的第M个集合是什么?
思路:设i个不同元素组成的非空字典序子集为kind[i],通过递推关系计算出kind[i] = i * (kind[i] + 1)可从计算式上推倒。得到这个关系后就可以通过一位一位的枚举得到答案了。
代码如下:
#include <iostream> #include <cstring> using namespace std; int seq[25], idx; long long kind[25]; int vis[25]; void deal(int N, long long M) { bool finish = false; while (!finish) { for (int i = 1; i <= N; ++i) { if (M == 1) { seq[++idx] = i; finish = true; break; } else if (M > (kind[N-1] + 1)) { M -= kind[N-1] + 1; } else { seq[++idx] = i; M -= 1; N -= 1; break; } } } } int main() { int N; long long M; kind[1] = 1; for (int i = 2; i <= 20; ++i) { kind[i] = i * (kind[i-1] + 1); // i个不同元素集合的非空字典序子集个数 } while (cin >> N >> M) { memset(vis, 0, sizeof (vis)); idx = -1; deal(N, M); int first = 1; for (int i = 0; i <= idx; ++i) { int x; // seq记录的是一个伪序列,即确定一个数字后又将后面的序列重排 for (int j = 1; j <= N; ++j) { if (!vis[j]) --seq[i]; if (!seq[i]) { if (first) { cout << j; first = 0; } else cout << " " << j; vis[j] = 1; break; } } } cout << endl; } return 0; }