Ban or Pick, What’s the Trick(2022 CCPC 绵阳站)
A. Ban or Pick, What’s the Trick
time limit per test
4 seconds
memory limit per test
1024 megabytes
Bobo has recently learned how to play Dota2. In Dota2 competitions, the mechanism of banning/picking heroes is introduced, modified and simplified as follows for the sake of the problem:
Suppose a game is played between two teams: Team A and Team B. Each team has a hero pool of nn heroes with positive utility scores a1,…,ana1,…,an and b1,…,bnb1,…,bn, respectively. Here we assume all heroes in two teams’ hero pool are distinct.
The two teams then perform ban/pick operations alternately, with Team A going first. In one team’s turn, it can either pick a hero for itself, or ban an unselected hero from the opponent’s hero pool.
After 2n2n turns, all heroes are either picked or banned. Each team then needs to choose at most kk heroes from all heroes it picked to form a warband and the score for the warband is calculated as the sum of utility scores over all heroes in it.
Let sA,sBsA,sB be the score of the warband formed by Team A and Team B, respectively. Team A wants to maximize the value of sA−sBsA−sB while Team B wants to minimize it.
Bobo wants to know, what should be the final value of sA−sBsA−sB, if both teams act optimally? He’s not really good at calculating this, so he turned to you for help.
Input
The first line contains two integers n,k(1≤n≤105,1≤k≤10)n,k(1≤n≤105,1≤k≤10).
The second line contains nn integers a1,a2,…,ana1,a2,…,an (1≤ai≤108)(1≤ai≤108), denoting the utility score of heroes for Team A.
The third line contains nn integers b1,b2,…,bnb1,b2,…,bn (1≤bi≤108)(1≤bi≤108), denoting the utility score of heroes for Team B.
Output
Output an integer in one line, denoting the answer.
Examples
Input
Copy
2 1
3 6
2 4
Output
Copy
2
Input
Copy
4 1
1 3 5 7
2 4 6 8
Output
Copy
0
Input
Copy
4 2
4 6 7 9
2 5 8 10
Output
Copy
3
题目分析:
DP
首先,这道题对于每个玩家来说,我有两种选择,要么选己方英雄,要么ban对方英雄
大家要想明白一点,我每轮回合操作,只对自己或对方队伍中最大的数操作
由于选的英雄被k限定,因此需要动态规划求解
因为k很小,我们可以选择三维dp,第一维操作次数,第二维玩家一目前为止所选英雄个数,玩家二目前为止所选英雄个数。
因为我们每一次对队伍中最大的数操作,我们用 :A玩家目前的可选最大数的位置=总个数-A之前选取的个数-B玩家ban的个数(轮数-B玩家选取英雄个数) 来表示。
由于每个人轮流操作,因此就把操作次数作为第一维度,通过取余是否为1来确认操作人
若使用dfs实现需要注意记忆化搜索,节约时间复杂度
同时也附上经典的for循环写法(我个人更喜欢)
dfs
#include <map>
#include <set>
#include <fstream>
#include <queue>
#include <deque>
#include <stack>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <cstdio>
#include <bitset>
#include <iomanip>
#define endl '\n'
#define int long long
#define Max(a, b) (((a) > (b)) ? (a) : (b))
#define Min(a, b) (((a) < (b)) ? (a) : (b))
#define BoBoowen ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
using namespace std;
const int N = 1e5 + 10;
int n, k;
int dp[2 * N][15][15];//注意第一维是操作总次数,所以开双倍
bool vis[2 * N][15][15];
vector<int> v1(N);
vector<int> v2(N);
int dfs(int x, int a, int b)
{
//记忆化搜索
if (x >= 2 * n)
{
return 0;
}
if (vis[x][a][b])
{
return dp[x][a][b];
}
vis[x][a][b] = true;
//计算玩家1目前所能取得的最大值的下标
int aa = x / 2 - b + a + 1;
// 计算玩家2目前所能取得的最大值的下标
int bb = (x + 1) / 2 - a + b + 1;
dp[x][a][b] = dfs(x + 1, a, b);//ban英雄的情况
if (x % 2 == 0)
{
if (aa <= n && a < k)//边界判定
{
dp[x][a][b] = max(dp[x][a][b], dfs(x + 1, a + 1, b) + v1[aa]);//该回合玩家选英雄的情况
}
}
else
{
if (bb <= n && b < k)
{
dp[x][a][b] = min(dp[x][a][b], dfs(x + 1, a, b + 1) - v2[bb]);
}
}
return dp[x][a][b];
}
int cmp(int x, int y)
{
return x > y;
}
signed main()
{
BoBoowen;
cin >> n >> k;
for (int i = 1; i <= n; ++i)
{
cin >> v1[i];
}
for (int i = 1; i <= n; ++i)
{
cin >> v2[i];
}
sort(v1.begin() + 1, v1.begin() + n + 1, cmp);
sort(v2.begin() + 1, v2.begin() + n + 1, cmp);
cout << dfs(0, 0, 0);
}
经典for循环递推
#include <map>
#include <set>
#include <fstream>
#include <queue>
#include <deque>
#include <stack>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <cstdio>
#include <bitset>
#include <iomanip>
#define endl '\n'
#define int long long
#define Max(a, b) (((a) > (b)) ? (a) : (b))
#define Min(a, b) (((a) < (b)) ? (a) : (b))
#define BoBoowen ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
using namespace std;
const int N = 1e5 + 10;
int dp[2 * N][15][15];
vector<int> v1(N);
vector<int> v2(N);
int cmp(int x, int y)
{
return x > y;
}
signed main()
{
BoBoowen;
int n, kk;
cin >> n >> kk;
for (int i = 1; i <= n; ++i)
{
cin >> v1[i];
}
for (int i = 1; i <= n; ++i)
{
cin >> v2[i];
}
sort(v1.begin() + 1, v1.begin() + n + 1, cmp);
sort(v2.begin() + 1, v2.begin() + n + 1, cmp);
for (int i = 2 * n - 1; i >= 0; --i)
{
if (i % 2 == 0)
{
for (int j = 0; j + 1 <= Min((i + 2) / 2, kk); ++j)
{
for (int k = 0; k <= Min((i + 1) / 2, kk); ++k)
{
int id = (i + 1) / 2 + j - k + 1;
dp[i][j][k] = Max(dp[i + 1][j][k], dp[i + 1][j + 1][k] + v1[id]);
}
}
for (int k = 0; k <= Min((i + 1) / 2, kk); ++k)
{
dp[i][Min((i + 2) / 2, kk)][k] = dp[i + 1][Min((i + 2) / 2, kk)][k];
}
}
else
{
for (int k = 0; k + 1 <= Min((i + 1) / 2, kk); ++k)
{
for (int j = 0; j <= Min((i + 1) / 2, kk); ++j)
{
int id = (i + 1) / 2 + k - j + 1;
dp[i][j][k] = Min(dp[i + 1][j][k], dp[i + 1][j][k + 1] - v2[id]);
}
}
for (int j = 0; j <= Min((i + 1) / 2, kk); ++j)
{
dp[i][j][Min((i + 1) / 2, kk)] = dp[i + 1][j][Min((i + 1) / 2, kk)];
}
}
}
cout << dp[0][0][0];
}