上海大学第21届ACM程序设计联赛(春季赛)部分题解

文章目录

  • 前言
  • [A.Antiamuny wants to learn binary search](#A.Antiamuny wants to learn binary search)
  • [B.Bespread with chequers](#B.Bespread with chequers)
  • [G.Golden jade matrix checker](#G.Golden jade matrix checker)
  • [H.How to know the function](#H.How to know the function)
  • [I.I like UNO !](#I.I like UNO !)
  • [J.Juxtaposed brackets](#J.Juxtaposed brackets)
  • 后记

前言

出于某些原因,模拟了一下2023年上海大学ACM校赛练练手,随便写了几个题,整理一下自己的题解。
题目地址

傻呗签到题,略。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int f(int l,int r,int x) { // l <= x <= r
	int cnt = 0;
	while(l <= r) {
		cnt++;
		int mid = (l + r) / 2;
		if (mid == x) 
			break;
		if (mid < x) 
			l = mid + 1;
		else 
			r = mid - 1;
	}
	return cnt;
}

int main(){
	int t;
	cin >> t;
	while(t--){
		int l,r,x;
		cin >> l >> r >> x;
		cout << f(l,r,x) << endl;
	}
} 

B.Bespread with chequers

稍加观察可以得到,对于 2 × n 2×n 2×n的情况下,可以由 2 × ( n − 1 ) 2×(n-1) 2×(n−1)的基础上拼一个 2 × 1 2×1 2×1的块,或者在 2 × ( n − 2 ) 2×(n-2) 2×(n−2)的基础上拼一个 2 × 2 2×2 2×2或两个 2 × 1 2×1 2×1的块,即 a n = a n − 1 + 2 a n − 2 a_{n}=a_{n-1}+2a_{n-2} an=an−1+2an−2

由于题目数据范围在 1 e 6 1e6 1e6内,但考虑到有 1 e 3 1e3 1e3组数据,考虑打表 ,时间复杂度 O ( n ) O(n) O(n)。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll maxn=1e6;
const ll mod=1e9+7;
int a[maxn+10];
int main(){
    int t;
    cin >> t;
    a[1]=1,a[2]=3;
    for(int i=3;i<=maxn;i++){
        a[i]=(2*a[i-2]%mod+a[i-1])%mod;
    }
    while(t--){
        int n;
        cin >> n;
        cout << a[n] << endl;
    }
}

G.Golden jade matrix checker

按照定义就是,给定一个 n × m n \times m n×m矩阵,要求内部所有数字之和为正数,任意一个 h × w h\times w h×w的子矩阵数字之和为负数。

考虑维护二维前缀和 ,令 p r e f i x [ i ] [ j ] prefix[i][j] prefix[i][j]表示 [ 1 ∼ i ] , [ 1 ∼ j ] [1\sim i],[1\sim j] [1∼i],[1∼j]的元素之和

,那么任意一个左上角为 [ i , j ] [i,j] [i,j]子矩阵的表示方法为
p r e f i x [ i + h − 1 ] [ j + w − 1 ] − p r e f i x [ i − 1 ] [ j + w − 1 ] − p r e f i x [ i + h − 1 ] [ j − 1 ] + p r e f i x [ i − 1 ] [ j − 1 ] prefix[i+h-1][j+w-1]-prefix[i-1][j+w-1]-prefix[i+h-1][j-1]+prefix[i-1][j-1] prefix[i+h−1][j+w−1]−prefix[i−1][j+w−1]−prefix[i+h−1][j−1]+prefix[i−1][j−1]

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll maxn=2e3;
const ll mod=1e9+7;
ll a[maxn+10][maxn+10];
ll prefix[maxn+10][maxn+10];//prefix[i][j]表示1~i/1~j之和 
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int n,m,h,w;
		scanf("%d%d%d%d",&n,&m,&h,&w);
		ll sum=0;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;++j){
				cin >> a[i][j];
				prefix[i][j]=0;
				sum+=a[i][j];
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				prefix[i][j]= prefix[i][j-1]+a[i][j];
			}
		}
		for(int j=1;j<=m;++j){
			for(int i=1;i<=n;++i){
				prefix[i][j]+=prefix[i-1][j];
			}
		}
		if(sum<=0){
			printf("NO\n");
			continue;
		}
		bool flag=true;
		for(int i=1;i+h-1<=n && flag;++i){
			for(int j=1;j+w-1<=m && flag;++j){//[i,j]~[i+h-1,j+w-1]
				ll sum=prefix[i+h-1][j+w-1]-prefix[i-1][j+w-1]-prefix[i+h-1][j-1]+prefix[i-1][j-1];
				if(sum>=0){
					flag=false;
					break;
				}
			}
		}
		printf("%s\n",flag?"YES":"NO");
	}
} 

H.How to know the function

傻呗签到题,只需注意到 n = 0 n=0 n=0时只需询问一次即可。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll maxn=2e3;
const ll mod=1e9+7;
ll a[maxn+10][maxn+10]; 
int main(){
	int t;
	cin >> t;
	while(t--){
		ll n;
		cin >> n;
		if(n==0){
			cout << "1" << endl;
		}else {
			cout << "2" << endl;
		}
	}
} 

I.I like UNO !

大模拟题,规则于普通UNO有所简化

需要注意几个点:

  1. 需要用 O ( 1 ) O(1) O(1)查询的结构去维护每个玩家的牌,我采用了二维数组计数的方式,将四个花色与13类牌按照优先级划分,构造出 4 × 13 4\times13 4×13的矩阵来维护。
  2. 牌库需要一直维护,玩家打过的牌会回收到牌库底,在原本的牌库被抽完一轮之后之前打过的牌就成为了牌库顶。我当时采用的数组双指针的方式去处理,但在最后一刻计算错了数组的大小导致最后两组数据一直不对卡了半个多小时。
  3. 当玩家有牌可出时,优先级是 "同花色但优先级更高的牌">"符号相同但颜色更高级的牌">"同花色但优先级更低的牌"
    举个例子,如果上家出的是蓝+2,那蓝0~蓝9>红+2>黄+2>蓝+2>绿+2>蓝反转>蓝跳过
  4. 当玩家无牌可出需要抽牌时,如果可以打出就必须立刻打出,如果是效果牌必须立刻结算
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll maxn=3e6;
const ll mod=1e9+7;
const int total=4;
struct Card{
	int type;//花色 
	int number;//标识 
};
int priorType(char c){//花色优先级 (数值越小越优先)
	if(c=='R'){
		return 1;
	}
	if(c=='Y'){
		return 2;
	}
	if(c=='B'){
		return 3;
	}
	if(c=='G'){
		return 4;
	}
	return 4;
}
int priorNumber(char c){//数字优先级 
	if(c>='0' && c<='9'){
		return 1+(c-'0');
	}
	if(c=='+'){
		return 11;
	}
	if(c=='R'){
		return 12;
	}
	if(c=='S'){
		return 13; 
	}
	return 13; 
}
int player[total+10][10][20];//玩家-花色-优先级 
Card deck[maxn+10];//牌堆
int handSize[total+10];//手牌数
int isWinning(){
	for(int i=1;i<=total;i++){
		if(handSize[i]==0){
			return i;
		}
	}
	return -1;
}
int getNextPlayer(int current,int sgn){//确定出牌下家 
	int nextPlayer=current;
	if(sgn==1){//正向
		nextPlayer++;
		if(nextPlayer>total){
			nextPlayer-=total;
		}
	}else{
		nextPlayer--;
		if(nextPlayer<1){
			nextPlayer+=total;
		}
	}
	return nextPlayer;
}
int main(){
	for(int i=1;i<=total;++i){
		for(int j=1;j<=5;j++){
			char temp[10];
			scanf("%s",temp);
			player[i][priorType(temp[0])][priorNumber(temp[1])]++;
		}
		handSize[i]=5;
	}
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		char temp[10];
		scanf("%s",temp);
		deck[i].type=priorType(temp[0]);
		deck[i].number=priorNumber(temp[1]);
	}
	int current=1;//当前出牌玩家
	int sgn=1;//下一位玩家的顺序(默认为正) 
	int deckTop=1;//当前牌堆顶 
	int deckBottom=n;//当前牌堆底 
	int lastType=deck[n].type;
	int lastNumber=deck[n].number;
	while(isWinning()==-1){
		bool flag=false;//是否出过牌 
		for(int i=1;i<=lastNumber-1 && !flag;i++){//相同花色数字牌
			if(player[current][lastType][i]>0){
					player[current][lastType][i]--;
					lastNumber=i;//更新前一张牌数字 
					handSize[current]--;
					flag=true;
					break;
			}
		}
		for(int i=1;i<=4 && !flag;i++){//相同数字的异色牌
			if(player[current][i][lastNumber]>0){
				player[current][i][lastNumber]--;
				lastType=i;//更新前一张牌花色 
				handSize[current]--;
				flag=true;
				break;
			}
		}
		for(int i=lastNumber+1;i<=13 && !flag;i++){//相同花色功能牌
			if(player[current][lastType][i]>0){
				player[current][lastType][i]--;
				lastNumber=i;//更新前一张牌数字
				handSize[current]--; 
				flag=true;
				break;
			}
		}
		if(flag){//如果打出了一张牌
			deckBottom++;
			deck[deckBottom].type=lastType;
			deck[deckBottom].number=lastNumber;//更新底部
			if(lastNumber>=1 && lastNumber<=10){//数字牌正常更新下家 
				current=getNextPlayer(current,sgn);
			} 
			else if(lastNumber==11){//+2牌,让下家抽2,并跳过 
				int nextPlayer=getNextPlayer(current,sgn);//确定出牌下家
				player[nextPlayer][deck[deckTop].type][deck[deckTop].number]++;
				player[nextPlayer][deck[deckTop+1].type][deck[deckTop+1].number]++;
				handSize[nextPlayer]+=2;//手牌数+2
				deckTop+=2;//抽2张牌
				current=getNextPlayer(nextPlayer,sgn);//下家被跳过 
			}
			else if(lastNumber==12){//反转牌 
				sgn=-sgn;
				current=getNextPlayer(current,sgn);//反向顺序 
			}
			else if(lastNumber==13){//跳过牌 
				int nextPlayer=getNextPlayer(current,sgn);
				current=getNextPlayer(nextPlayer,sgn);//下家被跳过 
			}
		}
		else{//没出牌,摸牌
			if(lastType==deck[deckTop].type || lastNumber==deck[deckTop].number){//抽到的牌可以立刻被打出 
				lastType=deck[deckTop].type;
				lastNumber=deck[deckTop].number;
				deckBottom++;
				deck[deckBottom].type=lastType;
				deck[deckBottom].number=lastNumber;//更新底部
				deckTop++;
				if(lastNumber>=1 && lastNumber<=10){//数字牌正常更新下家 
					current=getNextPlayer(current,sgn);
				}
				else if(lastNumber==11){//+2牌,让下家抽2,并跳过 
					int nextPlayer=getNextPlayer(current,sgn);//确定出牌下家 
					player[nextPlayer][deck[deckTop].type][deck[deckTop].number]++;
					player[nextPlayer][deck[deckTop+1].type][deck[deckTop+1].number]++;
					handSize[nextPlayer]+=2;//手牌数+2
					deckTop+=2;//抽2张牌
					current=getNextPlayer(nextPlayer,sgn);//下家被跳过 
				}
				else if(lastNumber==12){//反转牌 
					sgn=-sgn;
					current=getNextPlayer(current,sgn);//反向顺序 
				}
				else if(lastNumber==13){//跳过牌 
					int nextPlayer=getNextPlayer(current,sgn);
					current=getNextPlayer(nextPlayer,sgn);//下家被跳过 
				}
			}
			else{//还是没能打出牌 
				player[current][deck[deckTop].type][deck[deckTop].number]++;
				handSize[current]++;//手牌数+1
				deckTop++;
				current=getNextPlayer(current,sgn);//正常轮换下家 
			}
		}
	}
	printf("%c\n",'A'+isWinning()-1);
} 

J.Juxtaposed brackets

真没想到编译原理也可以出题啊,你怎么知道我编译原理LL(1)文法学的一坨

卡了半天没想通应该怎么写,题解也有点似懂非懂,先欠着慢慢思索吧。

后记

一共四小时,前一小时写了4个题,以为手感火热,结果J卡了好会儿,最后时间都顾着写大模拟题去了,有点幽默。

感觉自己突然年轻了几岁,好像回到过去熬夜打codeforces的时间了。

写完这篇博客就去好好复习编译原理。
DrGilbert 2024.3.8

相关推荐
Smile丶凉轩9 分钟前
微服务即时通讯系统的实现(服务端)----(1)
c++·git·微服务·github
肥猪猪爸18 分钟前
使用卡尔曼滤波器估计pybullet中的机器人位置
数据结构·人工智能·python·算法·机器人·卡尔曼滤波·pybullet
readmancynn30 分钟前
二分基本实现
数据结构·算法
萝卜兽编程32 分钟前
优先级队列
c++·算法
盼海40 分钟前
排序算法(四)--快速排序
数据结构·算法·排序算法
一直学习永不止步1 小时前
LeetCode题练习与总结:最长回文串--409
java·数据结构·算法·leetcode·字符串·贪心·哈希表
Rstln2 小时前
【DP】个人练习-Leetcode-2019. The Score of Students Solving Math Expression
算法·leetcode·职场和发展
芜湖_2 小时前
【山大909算法题】2014-T1
算法·c·单链表
珹洺2 小时前
C语言数据结构——详细讲解 双链表
c语言·开发语言·网络·数据结构·c++·算法·leetcode
孙同学要努力2 小时前
C++知识整理day1——前置基础知识整理(命名空间、输入输出、函数重载、引用)
开发语言·c++