问答媒体

 找回密码
 立即注册
快捷导航
搜索
热搜: 活动 交友 discuz
查看: 99|回复: 6

2022年CCPC绵阳-A. Ban or Pick, What's the Trick补题

[复制链接]

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2022-12-21 17:26:50 | 显示全部楼层 |阅读模式
题意:有两支队伍,各有由n个英雄组成的英雄池,价值分别为a1,a2....an,和b1,b2.....bn,双方最多可以选取k名英雄,双方队伍轮流操作,每次操作可以从己方英雄英雄池选一位英雄,或者从对面英雄池禁用一位对面没选择的英雄,队伍A最终获得的价值为Sa,队伍B最后获得的价值为Sb,A队伍想让Sa-Sb尽可能大,而B队伍想让Sa-Sb尽可能小,求最终结果。
思路:假设当前选择禁用英雄,一定是禁用对面当前价值最大的英雄,而选择英雄,则是选择当前己方价值最大的英雄,我们需要明确知道当前还剩下的价值最大的英雄是哪一个,而“还剩下”是指自己没有选并且对面没有禁用,如果我们知道双方选取了多少英雄,并且双方禁用了多少英雄,那么我们就知道了当前回合数,反之,我们知道了选了多少英雄,并且当前的回合数,就知道了当前禁用了多少英雄,就可以知道当前回合该选哪个英雄或者该禁用对面哪个英雄。k<=10暗示了我们需要将双方选取的英雄数量作为状态。当该队选取英雄数到达k时,后来只会禁英雄。
当前已经被禁用或者被选择的英雄的总数量为“对面已经操作总数”-“对面选的英雄数”+“自己选的英雄数”
nowa=roundb-numb+numa
nowb=rounda-numa+numb
考虑记忆化搜索 用数组dp[rounda][roundb][numa][numb],四个状态分别为a的操作数,b的操作数,a选择的英雄数,b选择的英雄数。
实际上也可以用数组dp[round][numa][numb],三个状态分别为双方操作数的总和,a选择的英雄数,b选择的英雄数。因为操作是轮流进行的,所以自然知道rounda和roundb。
dp数组记录的是Sa-Sb,当A行动时,他要让dp数组尽量大,当B行动时,他要让dp数组尽量小。
代码实现:
#include<bits/stdc++.h>
#define ll long long
const ll inf=0x3f3f3f3f3f3f3f3f;
using namespace std;
const int maxn=1e5+10;
int a[maxn],b[maxn];
int n,k;
ll dp[maxn*2][11][11];
ll dfs(int round,int numa,int numb)
{
        if(round==2*n)
        return 0;
        if(dp[round][numa][numb]!=-1)
        return dp[round][numa][numb];
        int nowa=round/2-numb+numa;//a已经被ban或被pick的数量
        int nowb=(round+1)/2-numa+numb;//b已经被ban或被pick的数量
        if(round%2==0)//现在a行动
        {
                ll ans=-inf;
                if(numa!=k&&nowa!=n)//已经选满k个英雄或无英雄可选
                ans=max(ans,dfs(round+1,numa+1,numb)+a[nowa+1]);
                ans=max(ans,dfs(round+1,numa,numb));
                return dp[round][numa][numb]=ans;
        }
        else//b行动
        {
                ll ans=inf;
                if(numb!=k&&nowb!=n)//已经选满k个英雄或无英雄可选
                ans=min(ans,dfs(round+1,numa,numb+1)-b[nowb+1]);
                ans=min(ans,dfs(round+1,numa,numb));
                return dp[round][numa][numb]=ans;
        }
}
int main()
{
        memset(dp,-1,sizeof dp);
        cin>>n>>k;
        for(int i=1;i<=n;i++)
        cin>>a;
        for(int i=1;i<=n;i++)
        cin>>b;
        sort(a+1,a+1+n,greater<int>());
        sort(b+1,b+1+n,greater<int>());
        cout<<dfs(0,0,0)<<'\n';
        return 0;
}
回复

使用道具 举报

0

主题

4

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-12-21 17:27:01 | 显示全部楼层
请问有m题的代码吗?不知道怎么实现的[捂脸]
回复

使用道具 举报

1

主题

3

帖子

3

积分

新手上路

Rank: 1

积分
3
发表于 2022-12-21 17:28:01 | 显示全部楼层
单调队列维护
回复

使用道具 举报

0

主题

4

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-12-21 17:28:32 | 显示全部楼层
队友写的
        string s; cin >> s;
        vector<char>stk;
        for (int i = 0; i < s.size(); i++) {
                if (s == 'S') {
                        while (!stk.empty() && stk.back() == 'P')stk.pop_back();
                }
                if (s == 'P') {
                        while (!stk.empty() && stk.back() == 'R')stk.pop_back();
                }
                if (s == 'R') {
                        while (!stk.empty() && stk.back() == 'S')stk.pop_back();
                }
                stk.push_back(s);
        }
        printf("%c\n", *stk.begin());
回复

使用道具 举报

0

主题

3

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2022-12-21 17:29:20 | 显示全部楼层
cf的gym有官方题解
回复

使用道具 举报

1

主题

5

帖子

6

积分

新手上路

Rank: 1

积分
6
发表于 2022-12-21 17:29:43 | 显示全部楼层
dp用全局的0(不memeset成-1),我T了,是因为dfs中有的状态是0吗(不能初始成0吗)
回复

使用道具 举报

1

主题

3

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2022-12-21 17:30:26 | 显示全部楼层
是这样的,但其实状态也有-1的情况,-1只是我的个人习惯,严谨点的话应该初始化为-inf什么的
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver| 手机版| 小黑屋| 问答媒体

GMT+8, 2025-7-7 18:21 , Processed in 0.103918 second(s), 23 queries .

Powered by Discuz! X3.4

Copyright © 2020, LianLian.

快速回复 返回顶部 返回列表