欢迎使用皮肤 Geek|

Tsawke

园龄:2年7个月粉丝:6关注:2

[ABC263F] Tournament 题解

[ABC263F] Tournament Solution

更好的阅读体验戳此进入

题面

给定 n,存在 2n 个人站成一排进行比赛,比赛赛制按照类满二叉树进行,即每 2i2i1 两人进行比赛,胜利者进入下一层继续按照相同赛制比赛,直至最终剩余一人。若第 i 人获得了 j 场比赛的胜利,那么将获得 Ci,j 的奖金。你可以任意安排每场比赛的输赢,以最大化所有人的奖金和,求最大值。

Solution

挺有意思的一道题,不难但是需要一点智慧。

显然 DP,但 DP 的状态有多种均可,这里主要介绍一个写起来较为简单易懂的。

dp(p,i) 表示比赛进行到了满二叉树节点的 p 节点处,此时还需要比赛 i 次,定义为还需要比赛主要是为了在叶子节点的时候便于处理。

发现这个东西和朴素线段树十分相似,则对于满二叉树的叶子节点编号 leaf,其代表的人的编号(这里从 1 开始编号)即为 leaf2n+1,也就是 leaf2n+1

所以对于所有叶子节点有 dp(leaf,i)=Cleaf2n+1,i,中间的转移也十分类似线段树,显然为:

dp(p,i)=max(dp(lsonp,i+1)+dp(rsonp,0),dp(lsonp,0)+dp(rsonp,i+1))

显然最终答案在根节点且剩余的比赛次数为 0,即 dp(1,0)

不难发现这个东西通过记忆化搜索来实现会更便捷一些。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

#define LS (p << 1)
#define RS (LS | 1)
#define MID ((gl + gr) >> 1)

template < typename T = int >
inline T read(void);

int N, powN;
ll dp[(1 << 16) << 2][20];
ll C[(1 << 16) + 10][20];

ll MakeDP(int p, int i){
    if(~dp[p][i])return dp[p][i];
    if(p >= N)return dp[p][i] = C[(p ^ N) + 1][i];
    return dp[p][i] = max(MakeDP(LS, i + 1) + MakeDP(RS, 0), MakeDP(LS, 0) + MakeDP(RS, i + 1));
}

int main(){
    memset(dp, -1, sizeof dp);
    N = 1 << (powN = read());
    for(int i = 1; i <= N; ++i)for(int j = 1; j <= powN; ++j)C[i][j] = read();
    printf("%lld\n", MakeDP(1, 0));
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

UPD

update-2022_12_30 初稿

本文作者:Tsawke

本文链接:https://www.cnblogs.com/tsawke/p/17032804.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Tsawke  阅读(20)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起