牛客练习赛121(上 ABC) 题解
代码风格
后续目标代码写在solve()方法中
#include<bits/stdc++.h>
//#include<iostream>
//#include<vector>
//#include<cstdio>
//#include<cmath>
//#include<cstring>
using namespace std;
#define ll long long
#define endl "\n"
const int N = 2e5 + 10;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int INF = 1e9;
const int MAXN = 2020;
const int mod = 1e8 + 7;
int prime[N];
void check() {
memset(prime, 0, sizeof(prime)); //0 素数 1 不是素数
prime[1] = 1;
for (int i = 2; i <= N; i++) {
if (prime[i] == 0) {
for (int j = i * 2; j <= N; j += i) {
prime[j] = 1;
}
}
}
}
bool cmp(int x, int y) {
return x > y;
}
void solve() {
}
int main() {
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin >> t;
// cin.ignore() ;
// cin.clear();
//t = 1;
//check();
//这里根据具体情况来变动t,是输入还是默认为1
while ( t--) {
solve();
}
}
A.小念吹气球
思路
题目分析后可以知道,这是将字母-数字进行创建键值对的方式,那么首次出现的字母对应的值就可以+2,其次+1即可,所以我们可以采用两种方式,一种是简单的静态固定大小数组即可解决,另一种我们采用键值对的形式即可完成
AC代码
//静态数组方式,因为是26个字母,所以进行-'a'或者-'A'来实现,但是如果同时存在就不行了
void solve() {
int n;
cin>>n;
string str;
cin>>str;
int s[27]={0};
ll sum=0;
for(int i=0;i<n;i++){
if(s[str[i]-'A']==0){
sum+=2;
s[str[i]-'A']++;
}else {
sum+=1;
s[str[i]-'A']++;
}
}
cout<<sum<<endl;
}
//使用stl的map方法直接解决
void solve() {
int n;
cin>>n;
string str;
cin>>str;
map<char,int>mp;
ll ans=0;
for(int i=0;i<n;i++){
if(mp.find(str[i])==mp.end()){//如果找不到
ans+=2;
mp.insert(make_pair(str[i],1));//使用make_pair方法或者直接裸键值对
//mp.insert({str[i],1});
}else{
ans+=1;
mp[str[i]]++;
//mp.at(str[i])++;
}
}
cout<<ans<<endl;
}
B.You Brought Me A Gentle Breeze on the Field
思路
题目分析后可以猜测该问题是博弈问题,那么对于此类博弈,一般从简单情况进行上推,而后利用贪心的方法进行破解,隧而求解,这里具体的解释,我放在了代码里,从简单形式,从后往前倒退等形式是十分关键的点
AC代码
void solve() {
int n,m,p;
cin>>n>>m>>p;
/*
简单情况:
1,先手方为连取方直接获胜 即 n>=2+1&&n<=2*m+1
2,先手方不为连取方获胜即 n>=1+1&&n<=m+1
3,n=1时 必然后手获胜 n=2时 必然先手获胜
分析可以知道,想要获胜必然是达到上述的简单步骤的时候才行
所以二者都在很聪明的情况下,都会去避免对方进入这个情况,所以一旦n很大的时候
都只会一个一个地取 这样结果就明朗了
*/
if(n==1){
cout<<"YangQiShaoNian"<<endl;
return;
}
if(n==2){
cout<<"XiaoNian"<<endl;
return;
}
if(p==0){
//xiaonian有连取机会
//那么n>2m+1后 每个人都只能1个1个来取
//而根据简单规则 即可得到
if(n>=3&&n<=2*m+1){
cout<<"XiaoNian"<<endl;
return;
}else{
//2m+1 >m+1 所以一个一个取一定是先达到连取方的破局点
cout<<"XiaoNian"<<endl;
return;
}
}else{
//YangQiShaoNian有连取机会
if(n>=2&&n<=m+1){
cout<<"XiaoNian"<<endl;
return;
}else{
//2m+1 >m+1 所以一个一个取一定是先达到连取方的破局点
cout<<"YangQiShaoNian"<<endl;
return;
}
}
}
C.氧气少年的水滴 2
思路
首先题意很容易读懂,那么如何处理是问题关键,我们观察数据可以发现测试案例与单次测试总量均在10^5数量级,那么意味着我们每一次的复杂度最多是O(n),所以我们分析完,可以采用模拟的方法进行即可,因为只有一滴水滴,所以一加之后,我们利用标记变量进行标记,然后左右变化即可,每一次有水滴后,我们从滴落点开始,每次分别用两个指针进行判断,这样复杂度就是O(n)
AC代码
//我这里写的是比较粗糙的,因为我当时考虑的是左一个右一个来处理,这样代码就很复杂了
void solve() {
int n, p;
int sd[N];
cin >> n >> p;
for (int i = 1; i <= n; i++)cin >> sd[i];
int lnum = 0, rnum = 0;//标识此刻左右漂浮的水滴数量
int ptr = p;
sd[ptr]++;
if (sd[ptr] == 10) {
lnum++;
rnum++;
for (int i = ptr - 1, j = ptr + 1; i > 0 || j <= n;) {
if (lnum == 0 && rnum == 0) { //这可以是一个结束条件,没有浮动的雨滴了
cout << "0 0" << endl;
return;
}
if (rnum != 0 && j > n && lnum == 0) {//提前终止,左结束,右存在
cout << lnum << " " << rnum << endl;
return;
}
if (lnum != 0 && i < 1 && rnum == 0) {//提前终止,左存在,右结束
cout << lnum << " " << rnum << endl;
return;
}
if (i > 0) {
if (lnum > 0) {
if (sd[i] + lnum >= 10) {//如果>=10 水滴就要破裂了
lnum = sd[i] + lnum - 10;
i--;
lnum++;
rnum++;
} else {//否则没有破裂就得把漂浮的水滴清空
sd[i] += lnum;
lnum = 0;
}
}
}
if (j <= n) {//右侧同理
if (rnum > 0) {
if (sd[j] + rnum >= 10) {
rnum = sd[j] + rnum - 10;
j++;
lnum++;
rnum++;
}else {
sd[j] += rnum;
rnum = 0;
}
}
}
}
cout << lnum << " " << rnum << endl;
} else {
cout << "0 0" << endl;
}
}
//一次性代码处理,极简版本
void solve() {
int n,p;
int sd[N];
cin>>n>>p;
for(int i=1;i<=n;i++)cin>>sd[i];//这里从1开始,方便计数
int lnum=0,rnum=0;//这里还除去了左右平行水滴不相撞的情况,如果相撞,增加标记变量即可
//lrnum llnum rlnum rrnum 分别表示左的左右水滴 右边的左右水滴 最后抵消即可
sd[p]++;
if(sd[p]==10){
lnum++;
rnum++;
int i=p-1,j=p+1;//两个指针指向一左一右
while((i>=1&&lnum>0)||(j<=n&&rnum>0)){//大循环结束条件是左右都遍历结束
//左继续 i>=1&&lnum>0
//右继续 j<=n&&rnum>0
//分别进行判断即可
while(i>=1&&lnum>0){//左边判断
if(sd[i]+lnum>=10){
lnum=sd[i]+lnum-10;
i--;
lnum++;
rnum++;
}else{
sd[i]+=lnum;
lnum=0;
}
}
while(j<=n&&rnum>0){//右边判断
if(sd[j]+rnum>=10){
rnum=sd[j]+rnum-10;
j++;
lnum++;
rnum++;
}else{
sd[j]+=rnum;
rnum=0;
}
}
}
}
cout<<lnum<<" "<<rnum<<endl;
}