RC-u1 睿抗,启动!
题目
你被委托开发一个用于睿抗机器人开发者大赛CAIP-编程技能赛的管理系统,这个管理系统需要一些账号名和密码,你需要按照规则根据账号生成对应的密码,具体规则是:
- 将当前操作的字符串初始化为提供的账号名。
- 每次生成会规定一个生成轮次 N。
- 对于每一轮次,按顺序执行以下操作:
- 对于当前操作的字符串,将所有大写字母替换为后一个字母;如将 A 替换为 B,B 替换为 C,以此类推。特别地,将 Z 替换为 A。对于所有小写字母,将其替换为前一个字母,如将 z 替换为 y,以此类推。特别地,将 a 替换为 z。
- 对于完成上一步后的字符串,如果连续出现了至少三个大写字母(不一定要相同),则将这些连续的大写字母全部改为小写字母;对于连续出现了至少三个小写字母(不一定要相同),则将这些连续的小写字母全部改为大写字母。注意修改不在原地进行,即修改结果不影响本次步骤中对于连续的判定。
现在给定账号名以及轮次,请你生成对应的密码。
思路
按照题意操作,改变字母大小写时,可以先把所有替换字母存入map中,找连续字母的时候用while循环。如果S为yourname,随便改一个字符串
代码
#include<bits/stdc++.h>
using namespace std;
unordered_map<char,char>mp;
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int n;
string e;
cin>>n>>e;
if(e=="yourname")e="zhenhao";
for(int i=0;i<25;i++)mp['A'+i]='B'+i,mp['b'+i]='a'+i;
mp['Z']='A';
mp['a']='z';
string s=e;
int m=s.size();
while(n--){
for(int i=0;i<m;i++){
if((s[i]>='a'&&s[i]<='z')||(s[i]>='A'&&s[i]<='Z'))
s[i]=mp[s[i]];
}
for(int i=0;i<m;i++){
int j=i;
while(s[j]>='a'&&s[j]<='z'){
if(j-i+1==3)
for(int k=i;k<=j;k++)s[k]=toupper(s[k]);
else if(j-i+1>3)s[j]=toupper(s[j]);
j++;
}
if(j-i>=3){
i=j-1;
continue;
}
j=i;
while(s[j]>='A'&&s[j]<='Z'){
if(j-i+1==3)
for(int k=i;k<=j;k++)s[k]=tolower(s[k]);
else if(j-i+1>3)s[j]=tolower(s[j]);
j++;
}
if(j-i>=3)i=j-1;
}
}
cout<<e<<'\n'<<s;
return 0;
}
RC-u2 桌游猜谜
题目
小 C 和他的朋友一起玩一个桌游。游戏开始的时候,一共有 6 种不同颜色的卡牌,每种颜色有 8 张卡牌,其中一面写着 1 到 8 的数字,每种颜色的每个数字的卡牌只有一张。每位玩家需要从每种颜色的卡牌中抽取一张,并将卡牌摆放在自己的面前,卡牌上的数字朝外,所有玩家坐成一圈,这样所有玩家能看见其他玩家卡牌上的颜色及数字,也能看见自己的卡牌的颜色,但看不到自己的卡牌的数字。
游戏会进行若干轮,每次轮到某位玩家的时候,玩家可以选择三种不同的颜色,再选择一个范围 [L,R],询问一次其他玩家,自己对应的三种颜色的卡牌的数字之和是不是在范围内。其他玩家可以如实地回答“是”或“不是”。在游戏最后玩家需要猜测出自己卡牌上的数字是什么。
为了提高自己的游戏水平,小 C 决定考虑一个简化的问题:现在游戏刚刚开始,小 C 第一个进行询问。不难发现,进行询问后,小 C 可以根据回答排除一些自己卡牌上的数字的不可能的情况。例如选择红色、黄色、蓝色三种颜色的卡牌,询问三种颜色卡牌上的数字和的范围是否为 [5,8],假设回答是“是”,那么显然不可能出现红色卡牌数字为 2、黄色卡牌数字为3、蓝色卡牌数字为 4 的情况。
进一步地,对于一个询问,假设其他玩家给出的回答为“是”的时候可以排除的自己卡牌数字的可能方案数为 K1,给出的回答为“不是”的时候可以排除的可能方案数为 K2。小 C 自然希望自己的询问能够在不同的情况下尽可能排除更多的情况,因此小 C 希望自己的询问能够最大化 Min(K1,K2) 的值。
请你求出在询问达到以上要求的情况下,小 C 卡牌数字剩余的可能方案数为多少。
思路
因为N最多为7,T最多为5,卡牌范围1到8,所以完全可以枚举所有可能情况。题目要求最大化MIN(K1,K2)的同时,输出最多的可能值,那么就是输出MAX(K1,K2)*pow(8-n,3),其中pow(8-n,3)就是其它三张牌的可能值
代码
#include<bits/stdc++.h>
using namespace std;
int b[10][10];
pair<int,int>ans;
pair<int,int>tmp;
void dfs(int x1,int x2,int x3,int cnt,int l,int r,int sum){
if(cnt==4){
if(sum>=l&&sum<=r)ans.first++;
else ans.second++;
return;
}
int t=-1;
if(cnt==1)t=x1;
else if(cnt==2)t=x2;
else t=x3;
for(int i=1;i<=8;i++){
if(b[t][i])continue;
dfs(x1,x2,x3,cnt+1,l,r,sum+i);
}
}
void check(){
if(min(tmp.first,tmp.second)<min(ans.first,ans.second)){
tmp=ans;
}
else if(min(tmp.first,tmp.second)==min(ans.first,ans.second)){
if(abs(tmp.first-tmp.second)<abs(ans.first-ans.second))
tmp=ans;
}
}
void solve(){
int n;
cin>>n;
tmp={0,0};
memset(b,0,sizeof b);
for(int i=1;i<=n;i++){
for(int j=1;j<=6;j++){
int x;
cin>>x;
b[j][x]=1;
}
}
for(int i=1;i<=6;i++){
for(int j=i+1;j<=6;j++){
for(int k=j+1;k<=6;k++){
for(int l=3;l<=24;l++){
for(int r=l;r<=24;r++){
ans={0,0};
dfs(i,j,k,1,l,r,0);
check();
}
}
}
}
}
cout<<max(tmp.first,tmp.second)*pow(8-n,3)<<'\n';
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}
RC-u3 兰州拉面派餐系统
题目
兰州拉面是著名美食,其煮面很有讲究,不同种类的面需要煮不同的时长。拉面馆的煮面师傅的规则很简单,只要手头有煮面篮子是空闲的,就把下一份客单指定的面放到空闲篮子里煮;如果空闲的篮子不止一个,那么先放到其中编号最小的篮子里煮;如果没有篮子是空闲的,就等待。一个篮子里的面煮够了时间,师傅要准时捞出来放到该客单对应的碗里,招呼服务员端给客人;如果有多个篮子里的面同时到了捞取时间,师傅会同时捞起,但要本着先到先得的原则,按下单顺序招呼送餐。
在生意火爆的拉面馆里,煮面师傅需要很强的记忆力和计时能力,才能面对源源不断的客单,同时照顾一大堆煮面篮子而不出错。如果面的品种不太多,篮子也不太多,那一位拉面师傅的大脑还是能应对的。但如果我们有上千种面、上万只煮面篮、数十万客单呢…… 这就需要请你帮忙,写个派餐系统来完成这个任务了。
思路
两个要求,一是如果有多个空篮子,放编号最小的篮子里煮;二是如果有多个篮子同时完成,先给编号小的客人
那就设两个优先队列,一个排序出篮时间和客人编号、一个排序篮子编号
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5,M=1e4+5;
int a[N]; //记录每种面时间
int b[M]; //记录每个锅用了几次
struct node{
int numb; //顾客编号
int id; //锅编号
int time; //时间
bool operator<(const node&x)const{
if(time==x.time)return numb>x.numb;
return time>x.time;
}
};
priority_queue<node>q;
priority_queue<int,vector<int>,greater<int>>p; //存空锅
bool flag;
void print(node t){
if(flag)cout<<" ";
flag=true;
cout<<t.numb<<':'<<t.time;
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int n,m,l;
cin>>n>>m>>l;
for(int i=1;i<=n;i++)cin>>a[i];
int tot=0,x;
int TIME=0; //记录当前时间
for(int i=1;i<=l;i++){
cin>>x;
if(tot<m){
q.push({i,++tot,TIME+a[x]});
b[tot]++;
continue;
}
if(!p.empty()){ //如果有篮子是空的
auto t=p.top();
p.pop();
q.push({i,t,TIME+a[x]});
b[t]++;
continue;
}
if(q.size()>1){ //找同一时间出锅
auto now1=q.top();
q.pop();
auto now2=q.top();
if(now1.time!=now2.time)q.push(now1);
else{
q.pop();
TIME=now1.time;
print(now1);
print(now2);
p.push(now1.id);
p.push(now2.id);
while(!q.empty()){
auto now3=q.top();
if(now3.time!=TIME)break;
q.pop();
print(now3);
p.push(now3.id);
}
}
}
if(!p.empty()){ //如果有篮子是空的
auto t=p.top();
p.pop();
q.push({i,t,TIME+a[x]});
b[t]++;
}
else{
auto t=q.top();
q.pop();
TIME=t.time;
print(t);
q.push({i,t.id,TIME+a[x]});
b[t.id]++;
}
}
while(!q.empty()){
auto t=q.top();
q.pop();
print(t);
}
cout<<'\n';
for(int i=1;i<=m;i++)cout<<b[i]<<("%c",i==m?'\n':' ');
return 0;
}
RC-u4 拆积木
题目
给定一个由带编号的积木搭成的长方体。其中每块积木的厚度都一样,由若干个单位边长的相邻方块组成(相邻是指两个方块有一面重合)。现在要求将这个长方体中的积木一块一块拆掉。每块积木只能从顶端取出,并且取出时不能移动还在长方体中的其它积木。请你给出一个拆积木的顺序。当然这种顺序可能是不唯一的,我们规定当有多种选择时,总是取出编号最小的那个。
思路
拓扑排序,将每个上行的编号与下行编号设路径
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5,M=1e6+5;
int mp[N][N],cnt;
int b[M]; //记录被覆盖的数量
bool vis[M];
int to[M],from[M],head[M],idx;
void add(int u,int v){
to[idx]=v,from[idx]=head[u],head[u]=idx++;
}
priority_queue<int,vector<int>,greater<int>>q;
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int n,m;
cin>>n>>m;
memset(head,-1,sizeof head);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>mp[i][j];
if(!vis[mp[i][j]]){
vis[mp[i][j]]=true;
cnt++; //记录编号数量
}
if(i!=1&&mp[i][j]!=mp[i-1][j]){
add(mp[i-1][j],mp[i][j]);
b[mp[i][j]]++;
}
}
}
memset(vis,false,sizeof vis);
for(int i=1;i<=m;i++){
if(b[mp[1][i]])continue;
if(vis[mp[1][i]])continue;
vis[mp[1][i]]=true;
q.push(mp[1][i]);
}
if(q.empty()){
cout<<"Impossible";
return 0;
}
int tot=0;
while(!q.empty()){
auto now=q.top();
q.pop();
if(tot++)cout<<' ';
cout<<now;
bool flag=true;
for(int i=head[now];i!=-1;i=from[i]){
int j=to[i];
if(--b[j]==0){
flag=false;
q.push(j);
}
}
if(tot==cnt)break;
if(q.empty()&&flag){
cout<<" Impossible\n";
return 0;
}
}
return 0;
}
RC-u5 栈与数组
题目
现在有两个大小分别为 C1 和 C2 的栈,以及一个固定长度的数组,数组的每个位置一开始都是空的。只要数组仍然有空间,每次你可以从任意一个栈顶取出一个数字放置在数组的任意空位,然后判断数组里是否有 K 个相同的数字,如果有的话,那么你可以从数组里删除这 K 个数字,删除后的空间可以继续用于放置其他数字。
请问如果要将两个栈全部清空,数组的长度至少是多少?
思路
dp[ i ] [ j ]第一个存栈当前容量,第二个存最大使用过的容量,然后每到一个dp[ i ] [ j ] ,它可能是从C1中取出一个新数字、也可能是从C2中取出一个新数字,所以要判断新数字能不能删除要从两个方面比较。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5,M=2e3+5;
int a1[N],a2[N];
int pre1[M],pre2[M];
pair<int,int>dp[N][N];
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin>>t;
while(t--){
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++)cin>>a1[i];
for(int i=1;i<=m;i++)cin>>a2[i];
memset(dp,0x3f3f3f3f,sizeof dp);
memset(pre1,0,sizeof pre1);
dp[0][1]=dp[1][0]={1,1};
for(int i=1;i<=n;i++){
for(int j=0;j<M;j++)pre2[j]=0;
for(int j=1;j<=m;j++){
int val1=1,val2=1;
if((pre1[a1[i]]+pre2[a1[i]]+1+(a1[i]==a2[j]))%k==0)val1-=k;
if((pre1[a2[j]]+pre2[a2[j]]+1+(a1[i]==a2[j]))%k==0)val2-=k;
int t1=max(dp[i-1][j].second,dp[i-1][j].first+1);
int t2=max(dp[i][j-1].second,dp[i][j-1].first+1);
if(t1!=t2){
if(t1<t2)
dp[i][j]={dp[i-1][j].first+val1,t1};
else
dp[i][j]={dp[i][j-1].first+val2,t2};
}
else{
int tmp=min(dp[i-1][j].first+val1,dp[i][j-1].first+val2);
dp[i][j]={tmp,t1};
}
pre2[a2[j]]++;
}
pre1[a1[i]]++;
}
cout<<dp[n][m].second<<'\n';
}
return 0;
}