嘘~ 正在从服务器偷取页面 . . .

CF1147F Zigzag Game 题解


CF1147F Zigzag Game 题解

题目链接:Zigzag Game

题意

这是一道交互题,并且有 $T$ 组数据。

给出一个两边均有 $n$ 个节点的完全二分图,左部点由 $1$ 到 $n$ 编号,右部点由 $n+1$ 到 $2n$ 编号。输入中会给出一个 $n \times n$ 的矩阵 $a$,其中 $a_{i,j}$ 表示连接 $i$ 和 $j+n$ 的边的边权。

现在有两个玩家 Alice 和 Bob 在这个图上玩游戏。首先 Alice 会选择 “increasing”(增加)或者 “decreasing”(减少)中的一个属性,Bob 则会自动选择另一个。接下来由 Alice 在图上选择一个起始节点 $p$ 放上一枚棋子,Bob 选择一条连接节点 $p$ 的边 $(p,q)$,将棋子移动到 $q$。

接下来由 Alice 先手轮流进行操作。每一次操作时操作方需要选择一个与当前节点 $x$ 相邻的未被经过的节点 $y$。设 $w$ 为 $(x,y)$ 的边权,$w’$ 为上一次移动时的边权,那么如果当前操作方在最开始选择 “increasing”,则需要满足 $w > w’$,否则需要满足 $w < w’$。然后当前操作方将棋子移动到 $y$。无法移动的操作方失败。

现在你需要与交互库模拟这样一盘游戏,包括选择先后(”Alice” 或者 “Bob”)、选择属性(”increasing” 或者 “decreasing”)、选择初始节点、选择移动节点和游戏过程。你需要保证在与交互库的游戏过程中获胜。

交互格式略。数据范围:$1 \le T,n \le 50,~1 \le a_{i,j} \le n^2$ 。

可以先去做一下 P4055 [JSOI2009] 游戏 ,是一道经典的二分图博弈题。

那么根据二分图博弈的经典结论,先手必败当且仅当原图存在完美匹配。

而这道题本身就是完美匹配,这使得我们考察后手是否仍存在必胜策略(选择后手的 Bob)。

显然我们需要构造一种匹配,使得任意两条匹配中的边 $(a,b),(c,d)$ 满足:

  • 我们取了 $(a,b)$,对方选择了 $(b,c)$ ,而我们可以继续选择 $(c,d)$ 。

设对方选择了 increasing (否则我们把边权取反即可),即我们选择了 decreasing 。

那么上述条件等价于,使得对于任意两条匹配中的边 $(a,b),(c,d)$ 满足

  • 若 $w(a,b) < w(b,c)$ ,则有 $w(c,d) < w(b,c)$ 。

可以发现,这个条件和稳定婚姻问题形式上是一致的。

对于匹配关系 $(a,b),(c,d)$ ,若 $b$ 认为 $c$ 比 $a$ 优,则 $c$ 不能认为 $b$ 比 $d$ 优,否则会导致不稳定。

即,若 $V(b,a) < V(b,c)$ ,则 $V(c,d) > V(c,b)$ 。其中 $V(i,j)$ 为 $i$ 对 $j$ 的评分/优先级。

如果我们钦定

  • 从 $a,c$ 一侧指向 $b,d$ 一侧的边按边权从小到大排序,即 $w(a,b_1) < w(a,b_2)$ 。
  • 从 $b,d$ 一侧指向 $a,c$ 一侧的边按边权从大到小排序,即 $w(b,a_1) > w(b,a_2)$ 。

那么可以得出:若 $w(b,a) < w(b,c)$ ,则 $w(c,d) > w(c,b)$ 。

而稳定婚姻问题是一定有解的,于是我们只需要构造出这样的匹配即可。

时间复杂度 $\mathcal{O}(n^2)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define mem(a) memset(a, 0, sizeof(a))
#define pb push_back
#define N ((int)(66))

char op; vector<int> vec[N], tmp;
int n, vis[N], pos[N], npy[N * 2], a[N][N], rk[N][N];
void solve(int type)
{
    mem(pos); mem(npy); rep(i, 1, n) vec[i].clear();
    static queue<int> q; while(!q.empty()) q.pop();
    rep(i, 1, n)
    {
        q.push(i); rep(j, 1, n) vec[i].pb(j);
        auto cmp = [&](int x, int y) { return (a[i][x] < a[i][y]) ^ type; };
        sort(vec[i].begin(), vec[i].end(), cmp);
    }
    rep(i, 1, n)
    {
        tmp.clear(); rep(j, 1, n) tmp.pb(j);
        auto cmp = [&](int x, int y) { return (a[x][i] > a[y][i]) ^ type; };
        sort(tmp.begin(), tmp.end(), cmp);
        rep(j, 0, n - 1) rk[i][tmp[j]] = j + 1;
    }
    while(!q.empty())
    {
        int u = q.front(); q.pop();
        while(!npy[u])
        {
            int v = vec[u][pos[u]++];
            if(!npy[v + n] || rk[v][npy[v + n]] > rk[v][u])
            {
                npy[npy[v + n]] = 0; if(npy[v + n]) q.push(npy[v + n]);
                npy[v + n] = u; npy[u] = v + n;
            }
        }
    }
}
void work()
{
    cin >> n; rep(i, 1, n) rep(j, 1, n) cin >> a[i][j];
    cout << "B" << endl; cin >> op;
    if(op == 'D') { rep(i, 1, n) rep(j, 1, n) a[i][j] = -a[i][j]; }
    int x; cin >> x; if(~x) { solve(x > n); cout << npy[x] << endl; } else return;
    cin >> x; while(~x) { cout << npy[x] << endl, cin >> x; }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int qwq; cin >> qwq; while(qwq--) work();
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/wwxaou4k

[2] https://www.luogu.com.cn/article/jtpnd77b


题外话

你说得对,但是

是谁把 CF3500 的题放到博弈论练习题里的?

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录