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

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 !
评论
  目录