【cf1315E】E. Double Elimination(dp)
题意:
现有\(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;
}
重要的是自信,一旦有了自信,人就会赢得一切。