@codeforces - [email protected] Zigzag Game

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

给定一个 2n 个结点的完全二分图,1~n 在左边,n+1~2n 在右边。第 i 个点与第 j+n 个点的边权为 aij,且 aij 互不相同。

Alice 与 Bob 在这个图上博弈。
一开始 Alice 选择 "increase" 或 "decrease",Bob 自动得到另一个。然后 Alice 选择点放置棋子,Bob 开始移动棋子,然后他们轮流移动棋子。但是不能移动到曾经到达过的点。

假如当前玩家选择 "increase",则该玩家接下来应该走一条比上一次边权大的边;反之如果选择 "decrease",应选一条比上一次小的边。
不能移动的人判负。

现在,你和评测机斗智斗勇。你可以选你是 Alice 还是 Bob,然后通过你自己的必胜策略战胜评测机。

(交互过程就不贴过来了,大家可以自己访问原题

@[email protected]

如果你有做过类似的题目(二分图上博弈,如 bzoj1443),你可能会更能够想到这道题的解法。

首先,你以为 Alice 既能决定 increase/decrease 又能决定起点很厉害?但是你手玩一下发现几乎都是 Bob 赢。
所以我们尝试构造出 Bob 的必胜策略。

对于这类二分图上博弈,必胜策略往往是顺着匹配边走。然而这道题边带权,我们不能求最大匹配。
我们尝试找到一类匹配,使得 Bob 沿着匹配边走是必胜的。以下假设 Alice 选择左侧点,并选择 increase,其他情况类似的。
Bob 沿着匹配边 (u, v) 前提是走这条匹配边 (u, v) 是合法的,即所有可能的博弈过程中经过 (u, v) 时前一条边都比这条边大。

假如说上一条匹配边为 (x, y),一种情况是 Alice 无法走 y -> u,即 w(x, y) > w(u, y)。
另一种情况选择走 y -> u。此时应该有 w(x, y) < w(u, y) 且 w(u, y) > w(u, v)。
即 w(x, y) > w(u, y) 或 w(u, y) > w(u, v) 时合法,反过来当 w(x, y) < w(u, y) < w(u, v) 时该匹配不合法。

带权的匹配?最大权匹配貌似不能应用于这道题。但是我们可以采用另一种带权匹配:稳定婚姻匹配(可以自行百度)。
我们把左边的点按 w 从小到大评估,右边的点按 w 从大到小评估,跑稳定婚姻匹配。
那么对于匹配的点对 (x, y) 与 (u, v),不会出现 w(u, v) > w(u, y) 且 w(x, y) < w(u, y) 的情况。即上述的不合法情况。

至于为什么 Bob 必胜,因为是完全图所以每个点都有匹配。
那么 Alice 无论走哪里,Bob 总可以找到对策。最后只可能 Alice 没有对策。

注意上面只讨论了一种情况:Alice 选择左侧点,并选择 increase。其他情况还需要进一步讨论。

@accepted [email protected]

#include <cstdio>
#include <cstdlib>
using namespace std;
const int MAXN = 50;
bool cmp(int x, int y, bool t) {
    if( !t ) return x > y;
    else return x < y;
}
// x 优于 y ?
int a[MAXN + 5][MAXN + 5], n;
bool tg[MAXN + 5][MAXN + 5];
int lnk[2*MAXN + 5];
void get_match(bool t) {
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            tg[i][j] = false;
    for(int i=1;i<=2*n;i++)
        lnk[i] = 0;
    while( true ) {
        bool flag = true;
        for(int i=1;i<=n;i++)
            if( !lnk[i] ) {
                int mx = 0;
                for(int j=1;j<=n;j++)
                    if( !tg[i][j] && (mx == 0 || cmp(a[i][j], a[i][mx], t)) )
                        mx = j;
                if( !lnk[mx+n] )
                    lnk[i] = mx + n, lnk[mx+n] = i;
                else if( cmp(a[i][mx], a[lnk[mx+n]][mx], !t) ) {
                    int x = lnk[mx+n];
                    lnk[i] = mx + n, lnk[mx+n] = i;
                    tg[x][mx] = true, lnk[x] = 0;
                }
                else tg[i][mx] = true;
                flag = false;
            }
        if( flag ) break;
    }
}
/*
男左女右
0 : 男大女小
1 : 女大男小
*/
void solve() {
    scanf("%d", &n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d", &a[i][j]);
    puts("B"), fflush(stdout);
    char str[5] = {}; int x;
    scanf("%s%d", str, &x);
    get_match( (str[0] == 'I') == (x <= n) );
    while( true ) {
        printf("%d\n", lnk[x]), fflush(stdout);
        int op;
        scanf("%d", &op);
        if( op == -1 ) return ;
        else if( op == -2 ) exit(0);
        else x = op;
    }
}
int main() {
    int t; scanf("%d", &t);
    while( t-- ) solve();
}

@[email protected]

一开始没有看到不能经过已经访问过的点,想了半天。。。

关于稳定婚姻匹配的过程,这里附上一个简要版的,供以后复习用:
左边的男生不断去尝试右边还没有拒绝过他且他最喜欢的女生,女生从当前追求她的男生中选择最喜欢的作为暂时的伴侣。最终一定可以得到稳定婚姻匹配。
首先所有人都会有匹配,否则一个男生一定追求了所有女生,而所有女生一旦被追求就不会单身。矛盾。
男生不可能会与更喜欢的女生私奔,因为之前肯定已经追求过了,而女生的伴侣一定是越来越优,所以他就越来越没有机会。

理论上是 O(N^2) 的,每一对关系只会被 check 一次(被拒绝过就不会再追求)
但是好像没人卡这个时间。。。

同时,这个匹配是男生最优 (male-optimal) 且女生最差 (female-pessimal) 的匹配方案。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11846799.html

时间: 2024-08-03 17:51:36

@codeforces - [email protected] Zigzag Game的相关文章

@codeforces - [email&#160;protected] Mashmokh&#39;s Designed Problem

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵 n 个点的树,每个点的儿子是有序的. 现给定 m 次操作,每次操作是下列三种中的一种: (1)给定 u, v,询问 u, v 之间的距离. (2)给定 v, h,断开 v 到父亲的边,将 v 这棵子树加入到它的第 h 个祖先的最后一个儿子. (3)给定 k,询问在当前这棵树上

@codeforces - [email&#160;protected] T-Shirts

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 有 n 件 T-shirt,第 i 件 T-shirt 有一个 ci 和 qi,分别表示费用与质量. 同时有 k 个顾客,第 j 个顾客准备了 bj 的金钱去购买 T-shirt. 每个顾客的购买策略是相同的: 他会买他的资金范围内 q 值最大的一件,如果有多个选 c 最小的一件,每种

@codeforces - [email&#160;protected] Oleg and chess

目录 @description - [email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @description - [email protected] 给定一个 n*n 的棋

@codeforces - [email&#160;protected] Lucky Tickets

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 已知一个数(允许前导零)有 n 位(n 为偶数),并知道组成这个数的数字集合(并不一定要把集合内的数用完).求有多少种可能,使得这个数前半部分的数位和等于后半部分的数位和. 模 998244353. input 第一行两个整数:n k.表示这个数的位数以及组成这个数的数字集合大小.2

@codeforces - [email&#160;protected] Bandit Blues

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 求有多少个长度为 n 的排列,从左往右遍历有 a 个数比之前遍历的所有数都大,从右往左遍历有 b 个数比之前遍历的所有数都大. 模 998244323. input 一行三个整数 n

@codeforces - [email&#160;protected] Vus the Cossack and a Field

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的 01 矩阵,通过这个矩阵生成一个无穷矩阵,具体操作如下: (1)将这个矩阵写在左上角. (2)将这个矩阵每位取反写在右上角. (3)将这个矩阵每位取反写在左下角. (4)将这个矩阵写在右下角. (5)将得到的矩阵再作为初始矩阵,重复这些操作. 比如对于初始矩阵:

@codeforces - [email&#160;protected] Big Problems for Organizers

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个点连成一棵树,经过每条边需要花费 1 个单位时间. 现给出 m 次询问,每次询问给出两个点,需要求所有点同时出发,最终所有点到达这两个点之一的最小花费时间. input 第一行包含一个整数 n (2?≤?n?≤?100000) ,表示点数. 接下来 n-1 行每行两个 1~n 的

@codeforces - [email&#160;protected] Strongly Connected Tournament

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个选手参加了一场竞赛,这场竞赛的规则如下: 1.一开始,所有选手两两之间独立进行比赛(没有平局). 2.主办方将胜者向败者连边形成 n 个点的竞赛图. 3.主办方对这个竞赛图进行强连通分量缩点. 4.每一个强连通分量内部的选手重复步骤 1~3,直到每一个强连通分量内只剩一个选手.

@codeforces - [email&#160;protected] Rotate Columns (hard version)

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的矩阵 A. 定义一次操作为将矩阵的某一列竖着循环移位,你可以对任意列做任意次操作. 定义 ri 为第 i 行的最大值,最大化 r1 + r2 + ... + rn. Input 第一行一个整数 t (1≤t≤40),表示数据组数. 每组数据第一行包含两个整数 n m