这场比赛我补了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;
}
大概题意为给你 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题的题意大概是给你 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;
}