E. Double Elimination (DP)

题目:传送门

题意:有 2^n 个人进行比赛,对他们编号 1~2^n,起初1和2打,3和4打,5和6打,7和8打...,然后1和2打完胜利的 和 3和4打完胜利的再打一场,1和2打完失败的和3和4打完失败的也再打一场,然后,失败组最终胜利的和胜利组最终胜利的再打一场。详情可看样例解释,结合着更容易理解。你有 k 个喜欢的队, 你想观看更多的你喜欢的队的比赛,你能决定比赛的胜利,问你最多能看多少场有你喜欢的队的比赛。

2 <= n <= 17 ,  0 <= k <= 2^n

题解: 这里有个视频的讲解 

#include <bits/stdc++.h>
#define LL long long
#define mem(i, j) memset(i, j, sizeof(i))
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define pb push_back
#define make make_pair
#define INF INT_MAX
#define inf LLONG_MAX
#define PI acos(-1)
using namespace std;

const int N = (1 << 17) + 5;
///设 dp[ i ][ j ][ f1 ][ f2 ] 为从 j 开始,连续的 2^(i - 1) 个人比赛,胜利组胜出的人 0/1(f1) 你喜欢的队,失败组胜出的人 0/1(f2) 你喜欢的队。
int dp[20][N][2][2];
int vis[N];
int main() {

    int n, k, x; scanf("%d %d", &n, &k);

    rep(i, 1, k) {
        scanf("%d", &x);
        vis[x - 1] = 1;
    }

    mem(dp, -0x3f3f3f);

    rep(i, 1, n) {
        for(int j = 0; j < (1 << n); j += (1 << i)) {
            if(i == 1) {
                rep(x1, 0, 1) rep(x2, 0, 1) {
                    if((x1 + x2) != (vis[j] + vis[j + 1])) continue;
                    dp[i][j][x1][x2] = ((x1 + x2) > 0 ? 1 : 0);
                }
            }
            else {
                rep(x1, 0, 1) rep(y1, 0, 1) rep(x2, 0, 1) rep(y2, 0, 1) {
                    ///x1,y1是从j开始连续的2^(i-1)个人比完后胜利者剩下的那个人的状态和失败组剩下的那个人的状态 (0:代表不是我喜欢的球队,1:代表是我喜欢的球队)
                    ///x2,y2是从j+(1<<(i-1))开始连续的2^(i-1)个人比完后剩下的那个人的状态和失败者剩下的那个人的状态
                    ///那么会有两次比赛,x1跟x2打,y1跟y2打,然后第二次有八种情况,逐个讨论
                    int res = dp[i - 1][j][x1][y1] + dp[i - 1][j + (1 << (i - 1))][x2][y2];
                    if(x1 || x2) res++; ///第一次比赛,先考虑
                    if(y1 || y2) res++;
                    
                    ///失败者最终胜利的是x2,那么有两种情况
                    dp[i][j][x1][x2] = max(dp[i][j][x1][x2], res + ( ( x2 + y1 > 0 ) ? 1 : 0 ) ); ///第一次比赛时失败组胜出的是y1,然后y1会跟x2再比一场
                    dp[i][j][x1][x2] = max(dp[i][j][x1][x2], res + ( ( x2 + y2 > 0 ) ? 1 : 0 ) ); ///第一次比赛时失败组胜出的是y2,然后y2会跟x2再比一场

                    /// 失败者最终胜利的分别是y1和y2,第一次比赛时胜利者胜出的是x1
                    dp[i][j][x1][y1] = max(dp[i][j][x1][y1], res + ( ( y1 + x2 > 0 ) ? 1 : 0 ) );///若第一次比赛y1胜出,y1会跟x2再比一场
                    dp[i][j][x1][y2] = max(dp[i][j][x1][y2], res + ( ( y2 + x2 > 0 ) ? 1 : 0 ) );///若第一次比赛y2胜出,y2会跟x2再比一场
                    
                    ///下同,只是换成了最终胜利的是x2
                    dp[i][j][x2][x1] = max(dp[i][j][x2][x1], res + ( ( x1 + y1 > 0 ) ? 1 : 0 ) );
                    dp[i][j][x2][x1] = max(dp[i][j][x2][x1], res + ( ( x1 + y2 > 0 ) ? 1 : 0 ) );

                    dp[i][j][x2][y1] = max(dp[i][j][x2][y1], res + ( ( y1 + x1 > 0 ) ? 1 : 0 ) );
                    dp[i][j][x2][y2] = max(dp[i][j][x2][y2], res + ( ( y2 + x1 > 0 ) ? 1 : 0 ) );
                }
            }
        }
    }

    int ans = 0;
    rep(x1, 0, 1) rep(y1, 0, 1) {
        int res = dp[n][0][x1][y1];
        if(x1 || y1) res++;
        ans = max(ans, res);
    }
    printf("%d\n", ans);

    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Willems/p/12376473.html