RC-u1 大家一起查作弊(字符串处理)
分数 15 作者 DAI, Longao 单位 杭州百腾教育科技有限公司
在今年的睿抗比赛上,有同学的提交代码如下:
public asfiasfgwef12(){
int tsadflas=3;
int masf11233=2;
int[]wasdf1213= new int[10 +1];
int[] vasf124l = new int[10 + I];
int[][] ddasf1234p= new int[masf11233
...
你肯定很奇怪,这看上去代码似乎不像是正常写出来的代码呀?没错,这是这位同学在网络上购买了所谓的“保研综测套餐”,商家为逃避赛后查重,给这位同学发去了经过混淆的代码。然而经过技术支持方的努力,这位同学不仅被封禁,与 TA 购买了相同“套餐”的同学也利用技术手段全部查出,目前主办方已向警方报案,这些同学的“保研”梦很有可能会转变为“案底”梦……因此如果你在比赛前也购买了类似的服务,现在迷途知返还来得及——毕竟这个商家起码还做了一些努力,许多商家号称“一对一”,实际上将一份代码发给了数十位同学……
回到题目,虽然具体检查的手段无法公开,但我们可以尝试简化再简化的手段来找到被这样混淆的程序。对于给定的大量字符串,你首先要提取出所有的关键词。一个关键词的定义是:由大写字母、小写字母、数字组成的字符串,并且前后均为非大小写字母及数字(包含开头及换行符)。如以下的字符串:
int[] vasf124l = new int[10 + I];
关键词为:int
、vasf124l
、new
、int
、10
以及I
。
然后对于所有关键词,你需要计算可疑分数的和以及关键词平均长度。其中一个关键词的可疑分数如下定义:
- 如果一个关键词同时包含大写字母、小写字母、数字,则分数 + 5 分;
- 否则,如同时包含(大写字母、数字)或(小写字母、数字)的,分数 + 3 分;
- 否则,如同时包含(大写字母、小写字母)的,分数 + 1 分;
- 其余情况不加分。
对于给定的数据,请你输出所有关键词的可疑分数的和、长度的和以及数量。
输入格式
输入包含若干行字符串,表示待检测的程序。保证每行字符串的长度不超过 1000(除了一行最后的换行符外),输入总长度不超过 6×104,并且至少有一个关键词。
输出格式
对于输入的所有字符串,第一行输出可疑分数的和,第二行输出关键词的总长度以及关键词的个数(以避免计算平均值带来的浮点误差),用一个空格隔开。
输入样例
static void nnmMNBkf3kfa(){
int fefvB4=2;
int [][]fsdk9A=new int[fefvB4][fefvB4];
fsdk9A[0][0]=1;
for (int gfdgsUB3 = 0; gfdgsUB3 < fefvB4; gfdgsUB3++) {
for (int fdnbXZ8 = 0; fdnbXZ8<fefvB4-gfdgsUB3-1; fdnbXZ8++) {
fsdk9A[gfdgsUB3][fdnbXZ8+1]=fsdk9A[gfdgsUB3][fdnbXZ8]+gfdgsUB3+fdnbXZ8+2;
fsdk9A[gfdgsUB3+1][fdnbXZ8]=fsdk9A[gfdgsUB3][fdnbXZ8]+gfdgsUB3+fdnbXZ8+1;
break;
}
break;
}
}
输出样例
155
276 54
解法
比赛时用getline一直卡样例,后来用cin才过,不知道什么原因:)
Step1:依次读入每个字符串,按非数字/字母字符进行分割。
Step2:计算每个字符串的得分,并统计。
代码
#include<bits/stdc++.h>
using namespace std;
string str;
int ans,lens,sum;
vector<string> v;
int judge(char c){
if(c >= 'a' && c <= 'z') return 1;
else if(c >= 'A' && c <= 'Z') return 2;
else if(c >= '0' && c <= '9') return 3;
else return 4;
}
void process(string s){
int i = 0;
int len = s.length();
while(i < len){
if(judge(s[i]) != 4){
int j = i;
string res = "";
while(judge(s[j]) != 4){
res = res + s[j];
j++;
}
v.push_back(res);
i = j;
continue;
}
i++;
}
}
int getScore(string s){
bool f1 = false;
bool f2 = false;
bool f3 = false;
for(auto it : s){
if(judge(it) == 1) f1 = true;
else if(judge(it) == 2) f2 = true;
else if(judge(it) == 3) f3 = true;
}
if(f1 && f2 && f3) return 5;
if((f1 || f2) && f3) return 3;
if(f1 && f2) return 1;
else return 0;
}
int main(void){
while(cin>>str){
process(str);
}
for(auto it : v){
ans += getScore(it);
lens += it.length();
sum += 1;
}
cout<<ans<<endl;
cout<<lens<<" "<<sum;
return 0;
}
RC-u2 谁进线下了?II(模拟)
分数 20 作者 DAI, Longao 单位 杭州百腾教育科技有限公司
Xepa Legends 是一个第一人称射击类大逃杀(“吃鸡”)游戏,每局游戏共有 20 支 3 人小队参加,最后获胜的队伍被称为“捍卫者”。
最近 Xepa Legends 举行了亚太地区南赛区的线上比赛,争夺 7 个前往德国曼海姆参加线下赛的资格,国内共有 14 支队伍参与到了其中。因为比赛十分激烈,直到最后谁进了线下仍有巨大的疑问。小 K 喜欢的国内知名战队 DreamTear 因其队内选手杀马特表现不佳,正好卡在出线分数前后,请你赶紧帮帮小 K,计算一下最后的分数情况,看看他喜欢的战队出线了没有吧!
Xepa Legends 的常规赛共有 30 支队伍参加,被分为三组,进行 N 轮比赛,每轮由三组中的两组组成 20 支队伍的参赛阵容,进行若干场比赛,最后每个队伍会获得一个当轮比赛的排名。
对于每轮比赛,队伍会根据排名获得一个在当轮比赛的赋分:
排名 | 分数 |
---|---|
第一名 | 25 分 |
第二名 | 21 分 |
第三名 | 18 分 |
第四名 | 16 分 |
第五名 | 15 分 |
第六名 | 14 分 |
第七名 | 13 分 |
第八名 | 12 分 |
第九名 | 11 分 |
第十名 | 10 分 |
第十一名 | 9 分 |
第十二名 | 8 分 |
第十三名 | 7 分 |
第十四名 | 6 分 |
第十五名 | 5 分 |
第十六名 | 4 分 |
第十七名 | 3 分 |
第十八名 | 2 分 |
第十九名 | 1 分 |
第二十名 | 0 分 |
给定若干轮比赛队伍获得的当轮比赛排名,请你计算出队伍的赋分,并在若干轮比赛后计算出总赋分,从而最终确定 DreamTear 战队能否进入线下,还是只能耍耍花招了。
例如,
- DreamTear 战队在第一轮比赛中获得了第 17 名,第三轮比赛中获得了第 11 名,第四轮比赛中获得了第 11 名,那么 DreamTear 战队可获 3 + 9 + 9 = 21 分的赋分;
- KV 战队在第一轮比赛中获得了第 10 名,第三轮比赛中获得了第 2 名,第四轮比赛中获得了第 6 名,那么他们可获得 10 + 21 + 14 = 45 分的赋分。
注:本题与实际情况无关,所有比赛规则、队伍、队员名称均为虚构。
输入格式
输入第一行是一个正整数 N (≤20),表示有 N 轮比赛。
接下来有 N 部分输入,每部分是一轮比赛的情况。对每一场比赛,信息共分 20 行,第 i 行(i=1,⋯,20)给出的两个非负整数 c 和 p 表示编号为 c 的队伍在这轮比赛里获得了第 p 名。
数据保证所有给定的情况中,排名永远大于等于 1 且小于等于 20,队伍编号由 1 开始,不超过 30。
输出格式
输出若干行,按分数从大到小依次输出队伍的编号及该队所有轮次游戏结束后的总分。如分数相同,队伍编号较小的先输出。
注意由于统计的时候比赛可能并没有完全结束,所以每个队伍参加的比赛轮数不一定相同,此时仍然正常计分统计即可。不要输出未参赛的队伍分数。
输入样例
3
1 1
2 2
9 3
6 4
7 5
11 6
3 7
13 8
8 9
16 10
4 11
19 12
17 13
5 14
12 15
15 16
14 17
10 18
20 19
18 20
5 11
10 12
30 13
22 14
1 1
28 20
21 16
26 17
2 2
24 3
4 4
29 5
8 6
7 15
6 7
3 8
9 9
25 10
23 19
27 18
19 20
26 19
27 18
18 17
21 16
12 15
28 14
20 13
17 12
14 11
13 10
23 9
29 8
22 7
30 6
15 5
24 4
25 3
16 2
11 1
输出样例
1 50
2 42
11 39
24 34
16 31
6 29
9 29
25 28
29 27
3 25
4 25
8 25
13 22
30 21
7 20
15 19
22 19
5 15
17 15
14 12
23 12
10 10
12 10
19 8
20 8
21 8
28 6
26 4
27 4
18 3
解法
依题意构造出team结构体,进行排序并输出。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAX = 1e6;
struct team{
int score;
int id;
team(int score_,int id_){
score = score_;
id = id_;
};
team(){
score = 0;
id = -1;
};
};
bool cmp(team a, team b){
if(a.score != b.score) return a.score > b.score;
else return a.id < b.id;
}
vector<team> v;
team te[MAX];
int n;
int sco[21] = {0,25,21,18,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0};
int main(void){
cin>>n;
int len = 0;
for(int i = 0 ; i < n ; i ++){
for(int j = 1 ; j <= 20 ; j ++){
int c,p;cin>>c>>p;
te[c].id = c;
te[c].score += sco[p];
len = max(c,len);
}
}
for(int i = 1 ; i <= len ; i ++){
if(te[i].id != -1){
v.push_back(te[i]);
}
}
sort(v.begin(),v.end(),cmp);
int lenv = v.size();
for(int i = 0 ; i < lenv ; i ++){
cout<<v[i].id<<" "<<v[i].score<<endl;
}
return 0;
}
RC-u3 势均力敌(DFS)
分数 25 作者 陈越 单位 浙江大学
用 n>2 个不同的个位数字组成一个 n 位数,显然有 n! 个不同的结果。可以证明,这 n! 个数字可以被分为势均力敌的两组 —— 即平方和相等、且个数也相等的两组。
本题就请你用程序验证一下这个结论。
因为本题是一道简单题,所以规模很小,只考虑 n≤4 的情况。
输入格式
输入第一行给出正整数 n(2<n≤4),随后一行给出 n 个不同的、在区间 [1, 9] 内的个位数字,其间以空格分隔。
输出格式
将所有组成的 n! 个不同的 n 位数分为平方和相等、且个数也相等的两组。但你只需要输出其中一组就可以了。每个数字占一行,共输出 n!/2 行。
注意:解可能不唯一,输出任何一组解就可以。
输入样例
3
5 2 1
输出样例
125
512
251
解法
依题n<=4,数据量较小,可用DFS枚举。
Step1:枚举出所有可能的数字。
Step2:枚举所有可能的分组,找到第一种可行解后输出。
代码
#include<bits/stdc++.h>
using namespace std;
int a[5];
int n;
bool vis[5];
vector<int> v;
vector<int> temp;
void dfs(int dep,string s){ //枚举出所有可能的数字
if(dep == n){
int res = 0;
for(auto it : s){
res = res*10 + (it - '0');
}
v.push_back(res);
return;
}
for(int i = 0 ; i < n ; i ++){
if(!vis[i]){
vis[i] = true;
dfs(dep + 1, s + char('0' + a[i]));
vis[i] = false;
}
}
}
void dfs2(int dep, int sum1, int sum2){ //枚举所有可能的分组
if(dep == (int)v.size()){
if(sum1 == sum2){
for(auto it : temp){
cout<<it<<endl;
}
exit(0);
}
return;
}
temp.push_back(v[dep]);
dfs2(dep+1,sum1 + v[dep]*v[dep],sum2);
temp.pop_back();
dfs2(dep+1,sum1,sum2 + v[dep]*v[dep]);
}
int main(void){
cin>>n;
for(int i = 0 ; i < n ; i ++){
cin>>a[i];
}
dfs(0,"");
dfs2(0,0,0);
return 0;
}
RC-u4 City 不 City(Dijkstra)
分数 30 作者 陈越 单位 浙江大学
“City 不 City”因为一位外国友人保保熊直播旅游时总是用奇怪的腔调说“好 city,啊!”而走红中国社交网络,成为网络热梗。事实上,有一些叛逆的年轻人在旅行时会刻意避开网红打卡点,选择一些小众的特色地方小城镇,不追求 city,而喜欢说“好 country,啊”。
下面给定各个城镇的旅游热度和城镇间的旅行花销,请你为前来咨询的旅行者规划一条最经济的路线,并且尽可能避开热度很高的网红点。
输入格式
输入第一行首先给出 4 个正整数:n 和 m(1<n≤103,1≤m≤5n),依次为城镇数量(于是城镇编号从 1 到 n)和城镇间的通路条数;s 和 t 依次为旅行者的出发地和目的地的城镇编号。
随后一行给出 n 个不超过 100 的正整数,依次为 n 个城镇的旅游热度。
再后面是 m 行,每行给出一条通路连接的两个城镇的编号、这条通路的最小花销(其数值为不超过 103 的正整数)。通路是双向的,题目保证任一对城镇间至多给出一条通路。
同一行的数字间均以空格分隔。
输出格式
题目要求从 s 到 t 的最小花销路线;若这样的路线不唯一,则取途径城镇的最高旅游热度值最小的那条路线。
在一行中输出从 s 到 t 的最小花销、以及途经城镇的最高旅游热度值(若没有途经的城镇,则热度值为 0)。数值间以 1 个空格分隔,行首尾不得有多余空格。
若从 s 根本走不到 t,则在一行中输出 Impossible
。
输入样例 1
8 14 7 8
100 20 30 10 50 80 100 100
7 1 1
7 2 2
7 3 1
7 4 2
1 2 1
1 5 2
2 5 1
3 4 1
3 5 3
3 6 2
4 6 1
5 6 1
5 8 1
6 8 2
输出样例 1
4 50
样例解释
从 7 到 8 的最短路径有 3 条,其中 2 条都经过城镇 1,于是对应的最高旅游热度值是城镇 1 的热度值 100。解路径为 7->2->5->8,途径城镇 2 和 5,对应的最高旅游热度值是城镇 5 的热度值 50。在最短路径长度相等的情况下,取热度值小的解,故输出的热度值为 50。
输入样例 2
3 1 1 2
10 20 30
1 3 1
输出样例 2
Impossible
解法
本题是多权值的单源最短路问题,考虑使用Dijkstra来做。
需要更新路径的条件为:
A. 新路径比老路径更短
B. 新路径和老路径一样长,但是途径点的最大热度更小
Dijkstra状态转移方程如下:
if(!visited[j] && dist[j] > dist[f] + edge[f][j]){ dist[j] = dist[f] + edge[f][j]; val[j] = max(val[f],w[j]); }else if(!visited[j] && dist[j] == dist[f] + edge[f][j] && val[j] > max(val[f],w[j])){ dist[j] = dist[f] + edge[f][j]; val[j] = max(val[f],w[j]); }
代码
#include<bits/stdc++.h>
using namespace std;
const int MAX = 1e3 + 50;
const int INF = 0x3f3f3f3f;
int n,m,s,t;
int w[MAX];
int edge[MAX][MAX];// n <= 1000, 用邻接矩阵即可
int val[MAX],dist[MAX];
bool visited[MAX];
void Dijkstra(){
for(int i = 1 ; i <= n ; i ++){
dist[i] = edge[s][i];
if(edge[s][i] != INF) val[i] = w[i];
}
visited[s] = true;
for(int i = 1 ; i < n ; i ++){
int f = -1;
int min_ = INF;
for(int j = 1 ; j <= n ; j ++){
if(!visited[j] && dist[j] < min_){
min_ = dist[j];
f = j;
}
}
visited[f] = true;
for(int j = 1 ; j <= n ; j ++){
if(!visited[j] && dist[j] > dist[f] + edge[f][j]){
dist[j] = dist[f] + edge[f][j];
val[j] = max(val[f],w[j]);
}else if(!visited[j] && dist[j] == dist[f] + edge[f][j] && val[j] > max(val[f],w[j])){
dist[j] = dist[f] + edge[f][j];
val[j] = max(val[f],w[j]);
}
}
}
}
int main(void){
cin>>n>>m>>s>>t;
for(int i = 1 ; i <= n ; i ++){
cin>>w[i];
}
w[s] = w[t] = 0; //依题意,不考虑起点和终点的热度
memset(edge,INF,sizeof(edge));
memset(val,INF,sizeof(val));
for(int i = 0 ; i < m ; i ++){
int a,b,c;cin>>a>>b>>c;
edge[a][b] = c;
edge[b][a] = c;
}
Dijkstra();
if(dist[t] == INF){
cout<<"Impossible";
}else{
cout<<dist[t]<<" "<<val[t];
}
return 0;
}
RC-u5 贪心消消乐
分数 30 作者 陈越 单位 浙江大学
“消消乐”是以消去方块赢取分数的游戏。这里介绍一种超级简单的玩法:玩家每次按住并拖动鼠标,在屏幕上划出一个矩形,则矩形内的方块就被消去,玩家得到所有被消去的方块的分数之和。
每个方块上的小动物对应不同的得分,例如消去上图中的绿色青蛙得 2 分、消去紫色猫头鹰得 5 分、消去黄色小鸡得 9 分、消去蓝色小牛得 1 分、消去红色狐狸得 3 分、消去棕色小熊得 8 分。有些方块是冰块,消去冰块会被扣分,所以冰块上标注了负分。有些方块是黑洞,玩家的矩形内不能包含任何黑洞,否则所有分数都被黑洞吸走。在一个矩形被消去后,其上方的方块会掉落以填补空缺,而多出的空缺被黑洞填补。
本题请你帮助玩家实现一个基于贪心策略的自动消除程序,每次都争取获得最多的分数,直到无法继续获得更多的分数。
输入格式
每个输入包含 1 个测试用例。每个测试用例第 1 行给出一个不超过 100 的正整数 N,对应正方形游戏屏幕的高度和宽度。随后 N 行,每行给出 N 个数字,代表对应方块的分数,其中黑洞用 0 表示,小动物用正整数分表示,冰块用负整数分表示。数值均在 [−100,100] 区间内。
输出格式
每一步的消除策略占一行,格式为:
(x1, y1) (x2, y2) 得分
其中 (x1, y1)
为矩形左上角的横纵坐标,(x2, y2)
为矩形右下角的横纵坐标,得分
为消去这个矩形的得分。这里我们将游戏屏幕的左上角坐标定义为 (1,1),右下角坐标定义为 (N,N)。
最后一行给出总分。
注意:当有多个矩形同时对应最大得分时,优先选择 x1
最小的;如果 x1
一样,则优先选择 y1
最小的;如果 y1
也一样,则优先选择 x2
最小的;如果 x2
还是一样,则优先选择 y2
最小的。
输入样例
4
0 2 5 0
9 2 -6 2
-4 1 -4 3
-1 8 0 -2
输出样例
(1, 2) (2, 4) 15
(3, 1) (3, 1) 5
(4, 2) (4, 3) 5
(2, 4) (2, 4) 2
27
样例解释
游戏的初始界面如题面所示。消去过程如下:
解法
代码
0] 区间内。
输出格式
每一步的消除策略占一行,格式为:
(x1, y1) (x2, y2) 得分
其中 (x1, y1)
为矩形左上角的横纵坐标,(x2, y2)
为矩形右下角的横纵坐标,得分
为消去这个矩形的得分。这里我们将游戏屏幕的左上角坐标定义为 (1,1),右下角坐标定义为 (N,N)。
最后一行给出总分。
注意:当有多个矩形同时对应最大得分时,优先选择 x1
最小的;如果 x1
一样,则优先选择 y1
最小的;如果 y1
也一样,则优先选择 x2
最小的;如果 x2
还是一样,则优先选择 y2
最小的。
输入样例
4
0 2 5 0
9 2 -6 2
-4 1 -4 3
-1 8 0 -2
输出样例
(1, 2) (2, 4) 15
(3, 1) (3, 1) 5
(4, 2) (4, 3) 5
(2, 4) (2, 4) 2
27
样例解释
游戏的初始界面如题面所示。消去过程如下:
解法
代码
以后补:)