把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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;
}
posted @ 2025-09-29 22:33  high_skyy  阅读(6)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end