AtCoder practice contest B 归并排序 + 二分 / 贪心 + 信息熵

题意

传送门 AtCoder practice contest B Interactive Sorting

题解
归并排序 + 二分

对于 n = 26 n = 26 n=26 的情况,归并排序最坏情况下需要 99 99 99 次比较。对于 n = 5 n=5 n=5 的情况,比较 c 1 , c 2 c_1,c_2 c1,c2,再比较 c 3 , c 4 c_3,c_4 c3,c4,接着比较两组数据的最小者(不妨假设为 c 1 c_1 c1),此时得到了一个有序的三元组 c 1 , c 3 , c 4 c_1,c_3,c_4 c1,c3,c4,二分求解 c 5 c_5 c5 的位置并插入,此时 c 1 c_1 c1 右侧的元素至多为 3 3 3,再次二分插入 c 2 c_2 c2 即可。由于插入一个有序三元组需要二分比较的次数为 2 2 2,故总比较次数 3 + 2 + 2 = 7 3+2+2=7 3+2+2=7 满足条件。

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    auto cmp = [&](int a, int b) {
        cout << "? " << char(a + 'A') << ' ' << char(b + 'A') << endl;
        char c;
        cin >> c;
        return c == '<';
    };

    function<void(vector<int> &)> merge_sort = [&](vector<int> &a) {
        int n = a.size();
        if (n <= 1) {
            return;
        }
        int m = n / 2;
        vector<int> b(a.begin(), a.begin() + m);
        vector<int> c(a.begin() + m, a.end());
        merge_sort(b);
        merge_sort(c);

        for (int ai = 0, bi = 0, ci = 0; ai < n; ++ai) {
            if (ci == (int)c.size() || (bi < (int)b.size() && cmp(b[bi], c[ci]))) {
                a[ai] = b[bi++];
            } else {
                a[ai] = c[ci++];
            }
        }
    };

    int n, q;
    cin >> n >> q;

    auto output = [&](vector<int> &ord) {
        cout << "! ";
        for (int i : ord) {
            cout << char(i + 'A');
        }
        cout << endl;
    };

    if (n == 26) {
        vector<int> ord(n);
        iota(ord.begin(), ord.end(), 0);
        merge_sort(ord);
        output(ord);
    } else {
        vector<int> a{0, 1}, b{2, 3};
        merge_sort(a);
        merge_sort(b);
        if (!cmp(a[0], b[0])) {
            swap(a, b);
        }
        vector<int> c{a[0], b[0], b[1]};
        int x = n - 1;
        int lb = -1, ub = c.size();
        while (ub - lb > 1) {
            int mid = (lb + ub) / 2;
            if (cmp(x, c[mid])) {
                ub = mid;
            } else {
                lb = mid;
            }
        }
        c.insert(c.begin() + ub, x);
        lb = ub == 0, ub = c.size();
        while (ub - lb > 1) {
            int mid = (lb + ub) / 2;
            if (cmp(a[1], c[mid])) {
                ub = mid;
            } else {
                lb = mid;
            }
        }
        c.insert(c.begin() + ub, a[1]);
        output(c);
    }

    return 0;
}
归并排序 + 贪心 + 信息熵

n = 5 n=5 n=5 的情况,若不同二元组关系是独立的,可以贪心的将 5 ! 5! 5! 中排列不断地划分为合法集合与不合法集合,并使合法集合的最大值尽可能的小。即贪心的取信息熵最大的二元组进行询问。(关系不独立的情况下不确定正确性, 仅保证小数据集上通过测试)

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    auto cmp = [&](int a, int b) {
        cout << "? " << char(a + 'A') << ' ' << char(b + 'A') << endl;
        char c;
        cin >> c;
        return c == '<';
    };

    function<void(vector<int> &)> merge_sort = [&](vector<int> &a) {
        int n = a.size();
        if (n <= 1) {
            return;
        }
        int m = n / 2;
        vector<int> b(a.begin(), a.begin() + m);
        vector<int> c(a.begin() + m, a.end());
        merge_sort(b);
        merge_sort(c);

        for (int ai = 0, bi = 0, ci = 0; ai < n; ++ai) {
            if (ci == (int)c.size() || (bi < (int)b.size() && cmp(b[bi], c[ci]))) {
                a[ai] = b[bi++];
            } else {
                a[ai] = c[ci++];
            }
        }
    };

    int n, q;
    cin >> n >> q;

    auto output = [&](vector<int> &ord) {
        cout << "! ";
        for (int i : ord) {
            cout << char(i + 'A');
        }
        cout << endl;
    };

    if (n == 26) {
        vector<int> ord(n);
        iota(ord.begin(), ord.end(), 0);
        merge_sort(ord);
        output(ord);
    } else {
        vector<vector<int>> g(n, vector<int>(n, -1));
        auto judge = [&](vector<int> &a) {
            for (int i = 0; i < n; ++i) {
                for (int j = i + 1; j < n; ++j) {
                    if (g[a[i]][a[j]] == 0) {
                        return 0;
                    }
                }
            }
            return 1;
        };

        for (;;) {
            int c = 0;
            vector<int> a(n);
            iota(a.begin(), a.end(), 0);
            vector<vector<int>> sat(n, vector<int>(n));
            do {
                if (judge(a)) {
                    c += 1;
                    for (int i = 0; i < n; ++i) {
                        for (int j = i + 1; j < n; ++j) {
                            sat[a[i]][a[j]] += 1;
                        }
                    }
                }
            } while (next_permutation(a.begin(), a.end()));

            if (c == 1) {
                iota(a.begin(), a.end(), 0);
                do {
                    if (judge(a)) {
                        break;
                    }
                } while (next_permutation(a.begin(), a.end()));
                output(a);
                break;
            }

            int qi = -1, qj = -1;
            int mn = 0;
            for (int i = 0; i < n; ++i) {
                for (int j = i + 1; j < n; ++j) {
                    int d = abs(sat[i][j] - sat[j][i]);
                    if (qi == -1 || mn > d) {
                        qi = i, qj = j;
                        mn = d;
                    }
                }
            }
            if (!cmp(qi, qj)) {
                swap(qi, qj);
            }
            g[qi][qj] = 1;
            g[qj][qi] = 0;
        }
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值