【CCPC】2022年绵阳站部分题解(ACGM)

重现赛链接

这场比赛我补了A、C、G、M四道题。

A. Ban or Pick, What's the Trick

C. Catch You Catch Me

G. Let Them Eat Cake

M. Rock-Paper-Scissors Pyramid


A. Ban or Pick, What's the Trick

A题是一道比较需要考验思维的记忆化搜索题目,原题意思是a、b团队分别有n个英雄,每个英雄有自己的战斗力,每个阵营在自己的回合中可以有两种选择:

1、ban掉一个对方阵营中还没被选择的英雄;

2、选择一个己方阵营还没被ban的英雄。

k为每个阵营最多可以选择的英雄数量。

 Sa  为a阵营选择英雄后的总战斗力, Sb  则为b阵营选择英雄后的总战斗力,

求两个阵营在表现都最佳的情况下,Sa - Sb 的最终值。

思路:

记忆化搜索,在每次 dfs 的时候都选择当前阵营最优的选法。由于k<=10,所以我们数组可以开到三维,

即dp[1e5*2][10][10](*2是因为我们把每个阵营的回合都拆开来,这样在写dfs的时候好判断些)

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 100010, M = 600010, mod = 998244353;
const long long INF = 1e18;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef long long LL;
typedef pair<LL, LL> PLL;

LL n, m;
LL a[N], b[N], dp[N * 2][15][15];

bool com(LL a, LL b) {
	return a > b;
}

LL dfs(LL turn, LL cnta, LL cntb) {
	//两个阵营分别选了cnta、cntb个英雄
	if (turn == 2 * n)return 0;
	if (dp[turn][cnta][cntb] != -INF)return dp[turn][cnta][cntb];

	int ra = turn / 2 + cnta - cntb;//ra:a阵营被操作(ban或选)的英雄数(rb同理)
	int rb = (turn + 1) / 2 - cnta + cntb;//(turn+1)/2是b阵营最多被ban掉的英雄数量(turn/2则是a阵营的)

	if (turn % 2) {
		//b的回合
		LL res = INF;
         //b希望res的值最小化,所以是取min
		if (cntb < m && rb + ra <= 2 * n)res = min(res, dfs(turn + 1, cnta, cntb + 1) - b[rb + 1]);
		res = min(res, dfs(turn + 1, cnta, cntb));
		dp[turn][cnta][cntb] = res;
		return res;
	}
	else {
		//a的回合
		LL res = -INF;
          //a希望res的值最大化,所以是取max
		if (cnta < m && ra + rb <= 2 * n)res = max(res, dfs(turn + 1, cnta + 1, cntb) + a[ra + 1]);
		res = max(res, dfs(turn + 1, cnta, cntb));
		dp[turn][cnta][cntb] = res;
		return res;
	}
}

void init() {
	for (int i = 0; i <= 2 * n; i++)
		for (int j = 0; j <= m; j++)
			for (int k = 0; k <= m; k++)
				dp[i][j][k] = -INF;
}

int main() {
	cin >> n >> m;
	init();
	for (int i = 1; i <= n; i++)cin >> a[i];
	for (int i = 1; i <= n; i++)cin >> b[i];
	sort(a + 1, a + 1 + n, com);
	sort(b + 1, b + 1 + n, com);
	cout << dfs(0, 0, 0) << endl;
	return 0;
}

C. Catch You Catch Me

大概题意为给你 n 条路径构成一个无向树,结点编号为1~n。在这棵树中,结点 1 为出口,其他所有结点上都有一只蝴蝶。每一分钟,每只蝴蝶都会沿着一条边向 1 号结点方向移动。

主人公Bobo能做的是选择树中某一分钟的任何结点(包括第0分钟,所有的蝴蝶都在它们各自的结点上),并捕获该结点上的所有蝴蝶(Bobo不能捕获结点1上的蝴蝶,因为蝴蝶到达结点1后立即消失)。

求Bobo抓到所有的蝴蝶最少要操作几次。

思路:

设置 ans 为Bobo的操作次数

1、在第0分钟的时候,Bobo需要将 结点1 的所有临接点上的蝴蝶抓到(不然它们就跑了),所以 ans+= 结点1的临接点的个数;

2、在每个 结点1 的子树中,我们完全可以等同一深度的蝴蝶走到一起时再抓,所以每棵子树需要的操作数为子树的深度,所以 ans += 所有子树的深度。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
 
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PLL;
typedef pair<int,int> PII;
const int N=100010;
 
LL n,m;
int h[N],e[N*2],ne[N*2],idx;
 
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
 
vector<int>v;
LL res=0;
void dfs(int u,int fa,LL d){<br>  //d 为深度
    res=max(res,d);
    for(int i=h[u];i!=-1;i=ne[i]){
     int j=e[i];
     if(j==fa)continue;
        dfs(j,u,d+1);
    }
}
 
int main(){
    memset(h,-1,sizeof h);
    cin>>n;
    LL ans=0;
    for(int i=1;i<n;i++){
        int a,b;
        cin>>a>>b;
        if(a>b)swap(a,b);
        add(a,b);
        add(b,a);
        if(a==1){
            v.push_back(b);
        }
    }
 
    for(int i=0;i<v.size();i++){
        res=0;
        dfs(v[i],1,1);
    ans+=res;
    }
    cout<<ans<<endl;
 
return 0;
}

G. Let Them Eat Cake

G题的题意大概是给你 n 个人,每个人有自己的号码,每个人都希望能得到Bobo王给的蛋糕。

在一轮中,有:

1、如果队伍剩余一个人,那么Bobo王会直接给他蛋糕(不计回合数);

2、如果一个人的号码比相邻的人(可能为1个 可能为2个)小,那么他就会得到蛋糕,然后退出队伍,后面的人按照顺序往前填补空缺(这算一轮)。

问:Bobo王想让所有人都得到蛋糕,最少需要多少轮?

思路:

一开始我想错了,以为是号码要同时比旁边的人小才能拿到蛋糕,结果wa了好几发.....

用两个vector来记录当前还在队伍中的人的号码,拿到蛋糕的人就离开,不会再进入下一轮。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>

using namespace std;
typedef long long LL;
typedef pair<LL, LL> PLL;
typedef pair<int, int> PII;
const int N = 100010;

LL n, m;
vector<LL>a, b;
int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        LL x;
        cin >> x;
        b.push_back(x);
    }
    LL cnt = 0;
    while (true) {
        if (b.size() <= 1)break;
        for (int x : b)a.push_back(x);
        b.clear();

        for (int i = 0; i < a.size(); i++)
            if (!i && a[i] > a[i + 1])b.push_back(a[i]);
            else if (i == a.size() - 1 && a[i - 1] < a[i])b.push_back(a[i]);
            else if (a[i - 1]<a[i] && a[i]>a[i + 1])b.push_back(a[i]);

        a.clear();
        cnt++;
    }
    cout << cnt << endl;

    return 0;
}

M. Rock-Paper-Scissors Pyramid

R :石头           S :剪刀             P :布

Bobo设计了一个“石头剪刀布”金字塔,将初始字符串 s 按顺序放置在最下面的积木上,金字塔按以下规则演化:

如果紧挨着一个方块下方的两个方块具有相同的形状(即都是R、P或S),我们将在方块上放置相同的形状。

如果一个方块下面的两个方块形状不同,我们将把获胜的形状放在方块上。

每次给你一串字符串序列(由若干个‘S’ 、‘R’ 、‘P’ 组成),问你最后哪个形状会到达塔顶。

思路:

如果一个字母要赢到最后,那么它肯定是所有字母里赢得次数最多的。为什么呢?

假设我是布,我前面是石头,那么无论如何到最后一定会是我距离塔顶更近(我会比前面的石头高一层);

同理,如果我是剪刀,我后面是石头,那么最后我一定会送我后面的石头更上一层。

#include <iostream>
#include <cstring>
#include <algorithm>
 
using namespace std;
 
const int N = 1000010, M = 600010;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef long long LL;
typedef pair<LL, LL> PLL;
 
LL n, m;
char s[N];
bool check(char a, char b) {
    if (a == b)return false;
    if (a == 'S')return b == 'P';
    if (a == 'P')return b == 'R';
    if (a == 'R')return b == 'S';
    //返回a是不是赢了
}
 
int main() {
    cin >> n;
    while (n--) {
        scanf("%s", s + 1);
        int ans = 0, maxc = -1;
        int pos = 1;
        int len = strlen(s + 1);
        for (int i = 2; i <= len; i++) {
            if (s[i] == s[i - 1])continue;
            if (check(s[i], s[i - 1]))ans++;
            else ans--;
            if (ans > maxc) {
                pos = i;
                maxc = ans;
            }
        }
        if (maxc == -1)pos = 1;
        printf("%c\n", s[pos]);
    }
 
    return 0;
}

官方题解

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值