Ban or Pick, What’s the Trick(2022 CCPC 绵阳站)

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];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值