CF107C Arrangement & 51Nod 1579 席位安排 题目分析
题目概述
一个排列 \(n\),有 \(m\) 条约束,其中 \(x,y\) 代表 \(p_x>p_y\)。
请问这种排列的第 \(k\) 小是什么。
其中 \(n\leq 16,m\leq 100\)。
分析
思路跟 P5770 [JSOI2016] 无界单词 类似,都是一位一位确定的。
我们考虑状态压缩 \(dp\)。
首先我们枚举第 \(i\) 个位置,一遍一遍得出其所要坐的人。
剩下略。
代码
时间复杂度 \(\mathcal{O}(n^32^n)\),能过。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stdlib.h>
#include <vector>
#define int long long
#define N 20
#define M (1 << 16) + 5
using namespace std;
int n,m,k,cnt[M],a[N],p[N];
int f[M][N],t,b[N];
void getdp(int x) {
for (int i = 0;i < t;i ++)
for (int j = 0;j <= n;j ++) f[i][j] = 0;
f[0][0] = 1;
for (int i = 0;i < t;i ++)
for (int j = 0;j <= n;j ++) {
int y = cnt[i] + 1;
if (b[y]) {
if ((a[b[y]] & i) != a[b[y]]) continue;//不合要求
if ((i >> b[y] - 1) & 1) continue;//
if (b[y] == x) f[i | (1 << b[y] - 1)][y] += f[i][j];
else f[i | (1 << b[y] - 1)][j] += f[i][j];
}
else {
for (int k = 1;k <= n;k ++)
if (!(i & (1 << k - 1))) {
if ((a[k] & i) != a[k]) continue;
if (k == x) f[i | (1 << k - 1)][y] += f[i][j];
else f[i | (1 << k - 1)][j] += f[i][j];
}
}
}
}
signed main(){
cin >> n >> k >> m;
k -= 2000;
t = (1 << n);
for (int i = 0;i < t;i ++)
for (int j = 1;j <= n;j ++)
if ((i >> j - 1) & 1) cnt[i] ++;
for (int i = 1;i <= m;i ++) {
int x,y;
scanf("%lld%lld",&x,&y);
a[y] |= (1 << x - 1);//a[i]是一个二进制数,其中1的位表示这一位对应的
//座位上坐的人的辈分必须比i座位上坐的人的辈分大
}
for (int i = 1;i <= n;i ++) {
getdp(i);
int sum = 0;
for (int j = 1;j <= n;j ++) {
if (!b[j] && sum + f[t - 1][j] >= k) {
b[j] = i,p[i] = j;
k -= sum;
break;
}
sum += f[t - 1][j];
}
if (!p[i]) return puts("The times have changed"),0;
}
for (int i = 1;i <= n;i ++) printf("%lld ",p[i]);
return 0;
}

浙公网安备 33010602011771号