2019E1_H 妙妙趣排序

1 篇文章 0 订阅
1 篇文章 0 订阅

妙妙趣排序

题目

本题中:

一个即将排好序的序列定义为:将这个序列去除至多一个值后,新序列是严格递增的。

一个过滤器[u,v]定义为:一个序列a经过过滤器[u,v]后, a u = m i n ( a u , a v ) , a v = m a x ( a u , a v ) a_{u}=min(a_{u},a_{v}),a_{v}=max(a_{u},a_{v}) au=min(au,av),av=max(au,av),其他值不变。

妙妙趣排序器由k个过滤器组成,一个序列的妙妙趣排序需要依次经过排序器的k个过滤器。

那么请问,1~n的全排列中,有几个序列经过给定的妙妙趣排序后可以变成即将排好序的序列。

输入

第一行一个正整数t表示数据组数 ( 0 < t < 100 ) (0<t<100) (0<t<100)

接下来 t 组数据

每组数据第一行两个整数 n , k ( 2 ≤ n ≤ 50 , 0 ≤ k ≤ 10 ) n,k (2≤n≤50,0≤k≤10) n,k(2n50,0k10)

每组数据接下来k行,每行两个整数 u , v u,v u,v,表示一个过滤器 ( 1 ≤ u < v ≤ n ) (1≤u<v≤n) (1u<vn)

输出

每组数据输出一行,一个整数

输入样例
4
4 0
4 1
1 2
4 3
1 2
2 3
1 2
4 6
1 2
2 3
1 2
3 4
2 3
1 2
输出样例
10
14
24
24
思路

首先考虑一点,任意一个排列,如果排序后可以变成即将排好序的序列,那么他一定对应一个且仅一个即将排好序的序列。多个排列可能对应一个即将排好序的序列。也就是说,这是一个类似于函数多对一的关系。

那么,我们来枚举所有即将排好序的序列,反推其原始排列,这些原始排列一定不会重复。

所以,枚举+dfs即可。注意dfs过程中剪枝验证合法性、

枚举所有即将排好序的序列可以在 O ( n 2 ) O(n^{2}) O(n2) 内完成,dfs的复杂度大概为 O ( 2 k ) O(2^{k}) O(2k)

代码
#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;
typedef long long ll;
const int ms = 50 + 10;
const int inf = 0x3f3f3f3f;
int t, n, k;
pair<int, int> opt[ms];
int ans;
void dfs(int x, vector<int>& to)
{
    if (x == 0)
    {
        ans++;
        return;
    }
    int u = opt[x].first - 1, v = opt[x].second - 1;
    if (to[u] > to[v])return;
    dfs(x - 1, to);
    swap(to[u], to[v]);
    dfs(x - 1, to);
    swap(to[u], to[v]);
}
int main()
{
    cin >> t;
    while (t--)
    {
        ans = 0;
        scanf("%d%d", &n, &k);
        for (int i = 1; i <= k; ++i)
        {
            scanf("%d%d", &opt[i].first, &opt[i].second);
        }
        vector<int> to(n, 0);
        for (int i = 0; i < n; ++i)
        {
            for (int j = 0; j < n; ++j)
            {
                to[j] = j + 1;
            }
            for (int j = i; j > 0; --j)
            {
                swap(to[j], to[j - 1]);
                if (i >= 1 && j == i) continue;
                dfs(k, to);
            }
            for (int j = 0; j < i; ++j)
            {
                swap(to[j], to[j + 1]);
            }
            for (int j = i; j < n - 1; ++j)
            {
                swap(to[j], to[j + 1]);
                dfs(k, to);
            }
        }
        for (int j = 0; j < n; ++j)
        {
            to[j] = j + 1;
        }
        dfs(k, to);
        printf("%d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值