【cf1315E】E. Double Elimination(dp)

南笙酒味 提交于 2020-02-25 22:14:35

传送门

题意:
现有\(2^n,n\leq 17\)个参赛选手,开始\(2\cdot i,2\cdot i-1\)两两配对进行比赛。
比赛规则:每场比赛中赢的人会进入胜者组,输的人会进入败者组,一个人如果输两次那么直接出局。最终胜者组和败者组最终会只剩下一个人,决赛时只进行一场,赢的人就胜利。
现在你有\(k\)支心仪的队伍,你能够安排每场比赛的胜负,你希望看到尽量多的比赛中含有你的心仪队伍。
问这样的比赛数量最多为多少。
可以结合下图理解一下:

思路:
这个题初看不是很好思考,直接看了题解...接下来说说大概思路:

  • 因为共有\(2^n\)个人,结合题意可以考虑合并两个\(2^{i-1}\)\(2^i\)
  • 如果想到了\(dp\),那么问题就转化为怎么定义\(dp\)状态和进行状态的合并。
  • 最显然的想法就是\(dp_{i,j,k}\)表示长度为\(2^i\),起点为\(j\),最终剩下的队伍是否为心仪的队伍。但是这种状态的定义不能考虑到输掉一场的人。因为两段合并时,不仅有赢的跟赢的打,还有输的跟输的打,最终再打一场才能决定最后的那个人。
  • 因为上面的状态不能考虑到输的人,所以我们重新定义:\(dp_{i,j,f_1,f_2}\)表示长度为\(2^i\),起点为\(j\),胜者组最后的队伍是否为心仪队伍,败者组最后的队伍是否为心仪队伍。
  • 这样的话我们就可以考虑到所有的情况,只是需要在\(dp\)时手动枚举一下,最后一共有\(8\)种情况。
  • 最终决赛的时候再单独判断一下即可。

这个题难就难在状态的定义,以及想清楚比赛中所遇到的一些情况。
细节见代码:

/*
 * Author:  heyuhhh
 * Created Time:  2020/2/25 21:11:41
 */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = (1 << 17) + 5, M = 18;

int n, k;
int dp[M][N][2][2];
bool fan[N];

void run(){
    cin >> n >> k;
    for(int i = 1; i <= k; i++) {
        int x; cin >> x;
        fan[x] = 1;   
    }
    memset(dp, -INF, sizeof(dp));
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= (1 << n); j += (1 << i)) {
            if(i == 1) {
                dp[i][j][fan[j]][fan[j + 1]] = (fan[j] | fan[j + 1]);
                dp[i][j][fan[j + 1]][fan[j]] = (fan[j] | fan[j + 1]);
            } else {
                for(int x1 = 0; x1 < 2; x1++) {
                    for(int y1 = 0; y1 < 2; y1++) {
                        for(int x2 = 0; x2 < 2; x2++) {
                            for(int y2 = 0; y2 < 2; y2++) {
                                int cost = dp[i - 1][j][x1][y1] + dp[i - 1][j + (1 << (i - 1))][x2][y2];
                                if(x1 || x2) ++cost;
                                if(y1 || y2) ++cost;
                                
                                dp[i][j][x1][x2] = max(dp[i][j][x1][x2], cost + (x2 | y1));
                                dp[i][j][x1][x2] = max(dp[i][j][x1][x2], cost + (x2 | y2));
                                
                                dp[i][j][x1][y1] = max(dp[i][j][x1][y1], cost + (x2 | y1));
                                dp[i][j][x1][y2] = max(dp[i][j][x1][y2], cost + (x2 | y2));
                                
                                dp[i][j][x2][x1] = max(dp[i][j][x2][x1], cost + (x1 | y1));
                                dp[i][j][x2][x1] = max(dp[i][j][x2][x1], cost + (x1 | y2));
                                
                                dp[i][j][x2][y1] = max(dp[i][j][x2][y1], cost + (x1 | y1));
                                dp[i][j][x2][y2] = max(dp[i][j][x2][y2], cost + (x1 | y2));
                            }
                        }
                    }
                }
            }
        }   
    }
    int ans = 0;
    for(int i = 0; i < 2; i++) {
        for(int j = 0; j < 2; j++) {
            ans = max(ans, dp[n][1][i][j] + (i | j));
        }   
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!