第二届“传智杯”全国大学生IT技能大赛(初赛同步)
P6363 软件工程实习
题目描述
某大学的《软件工程》必修课分为理论和实践部分。理论部分由该校教授完成授课;而实践部分是由第三方的公司主导,需要同学们在五周时间内自学 HTML、css、JavaScript、vue、Python、django 等技术,并组队完成一个真实的互联网商业应用。
参与这个课程的有 \(n(0\le n \le 1000)\) 个学生,分成了不超过 \(26\) 个队伍,每个队伍用 \(A\) 到 \(Z\) 来表示。每个队伍都会完成一个项目,并以队伍为单位,给所有队伍(包括自己队伍)打分,范围是 \(0\) 到 \(100\) 的整数。
为了平息学生对这门课产生的出多问题的不满(比如工作量太大、时间过于紧张、考核方式不公平等),老师决定使用一种“看起来”很公平的方式来决定每组队伍的项目得分:
对于某个队伍,首先计算所有队伍(包括自己)给这个队伍评分的平均值,然后剔除掉和这个平均值差别超过 \(15\) 分的评分(保证不会出现所有的评分全部被剔除的情况),最后再对剩余的评分求平均值,四舍五入取整后作为这个队伍的项目得分。
对于每一个同学,我们已经得知他们的队伍代号和理论成绩(也是 \(0\) 到 \(100\) 分的整数)。这位同学的最后得分就是 \(60\%\) 的理论成绩加上 \(40\%\) 的所在队伍的项目得分,然后四舍五入取整。
现在老师想知道所有同学的分数排行。请按最后得分从高到低的顺序,输出每位同学的得分和他所在的队伍。
输入格式
第一行两个整数 \(n\) 和 \(k\),分别表示学生数和队伍数。
接下来 \(n\) 行,每行一个整数 \(s_i\) 和一个大写字母 \(c_i\) ,表示第 \(i\) 个选手的理论分数和队伍编号。保证队伍编号范围是从 \(A\) 开始后连续 \(k\) 个字母中的一个。
接下来 \(k\) 行,每行 \(k\) 个整数。其中第 \(i\) 行第 \(j\) 列的整数 $a_{i,j} 表示第 \(i\) 个队伍给第 \(j\) 个队伍的评分,当 \(i=j\) 时,就是自评。
输出格式
输出 \(n\) 行表示答案,得分高的同学优先输出,得分相同时队伍编号较小的同学优先输出。对于每一行,先输出这位同学成绩,然后输出代表他的队伍编号的大写英文字母。
输入
6 3
70 A
80 A
65 B
95 B
85 C
90 C
70 90 100
95 88 85
30 47 100
输出
93 B
92 C
89 C
76 A
75 B
70 A
说明/提示
\(A\) 组收到的分数分别是 \(70,95,30\),其平均分为 \(65\),则 \(95,30\) 被作为无效成绩剔除,因此 \(A\) 组的项目得分是 \(70\) 分。
\(B\) 组收到的分数分别是 \(90,88,47\),其平均分为 \(75\),则 \(47\) 被作为无效成绩剔除,因此 \(B\) 组的项目得分是 \(89\) 分。
\(C\) 组收到的分数分别是 \(100,85,100\),其平均分为 \(95\),没有打分被剔除,因此 \(C\) 组的项目得分是 \(95\) 分。
解题思路
模拟
模拟题,注意四舍五入~
- 时间复杂度:\(O(nlogn+k^2)\)
代码
#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
int n,k,score[30][30],final_score[30],sum[30];
pair<int,int> res[1005],can[1005];
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
int s;
char c;
scanf("%d %c",&s,&c);
can[i]={s,c-'A'+1};
}
for(int j=1;j<=k;j++)
for(int i=1;i<=k;i++)
{
scanf("%d",&score[i][j]);
sum[i]+=score[i][j];
final_score[i]=sum[i];
}
for(int i=1;i<=k;i++)
{
int cnt=k;
for(int j=1;j<=k;j++)
if(abs(sum[i]-k*score[i][j])>k*15)final_score[i]-=score[i][j],cnt--;
final_score[i]=int(1.*final_score[i]/cnt+0.5);
}
for(int i=1;i<=n;i++)
res[i]={int(0.6*can[i].fi+0.4*final_score[can[i].se]+0.5),can[i].se};
sort(res+1,res+1+n,[](auto &a,auto &b){if(a.fi!=b.fi)return a.fi>b.fi;return a.se<b.se;});
for(int i=1;i<=n;i++)
printf("%d %c\n",res[i].fi,res[i].se-1+'A');
return 0;
}
P6364 1024 程序员节发橙子
题目描述
每年的 1024 程序员节日,黑马程序员都会举办大型的庆祝活动。今年的程序员节也不例外,每个班级的同学都发了橙子。
班级里有 \(n\) 名同学从前到后排成一排,且已经得知了这些同学的成绩,其中第 \(i\) 名同学的成绩是 \(a_i\)。班主任想根据同学们上个阶段的考试成绩来评定发橙子的数量。为了激励成绩优秀同学,发橙子时需要满足如下要求:
- 相邻同学中成绩好的同学的橙子必须更多。若相邻的同学成绩一样,则它们分到的数量必须平等。
- 每个同学至少分配一个橙子
由于预算有限,班主任希望在符合要求的情况下发出尽可能少的橙子。请问,至少需要准备多少橙子呢?
输入格式
第一行是一个整数 \(n\),表示学生数量。
接下来一行有 \(n\) 个整数,第 \(i\) 个整数 \(a_i\),表示第 \(i\) 个同学的成绩。
输出格式
输出答案,也就是需要最少准备多少个橙子。
输入
5
3 4 5 4 3
输出
9
说明/提示
样例 1 解释
每位同学拿到的橙子的数量分别是 \(1,2,3,2,1\),所以至少需要准备 \(9\) 个。
数据规模与约定
对于全部的测试点,保证 \(1 \leq n \leq 10^6\) ,\(0 \leq a_i \leq 10^9\)。
解题思路
记忆化搜索
同题:135. 分发糖果
注意:结果会爆int
- 时间复杂度:\(O(n)\)
代码
#include<bits/stdc++.h>
using namespace std;
int f[1000005],a[1000005],n;
int dfs(int x)
{
if(f[x]!=-1)return f[x];
f[x]=1;
if(x+1<=n&&a[x]>a[x+1])f[x]=max(f[x],dfs(x+1)+1);
if(x-1>=1&&a[x]>a[x-1])f[x]=max(f[x],dfs(x-1)+1);
return f[x];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
memset(f,-1,sizeof f);
long long res=0;
for(int i=1;i<=n;i++)res+=dfs(i);
printf("%lld",res);
return 0;
}
P6365 众数出现的次数
题目描述
传智专修学员的课堂上,为了活跃气氛,并巩固位运算的知识,同学们玩起了一个游戏。
班级里有 \(n(n\le10^6)\) 名同学,每位同学都获得了两张卡,红卡或者黑卡。每张卡上都有一个不超过 \(10^9\) 的非负整数。第 \(i\) 位同学手里红卡数字是 \(a_i\) ,黑卡数字是 \(b_i\)。
现在需要每位同学出牌。每位同学可以直接将红卡上的数字打出,或者将自己的红卡上的数字和自己黑卡数字进行按位异或操作后的结果打出。最后老师会收集所有同学打出的数字。
这些数字中出现次数最多的数字是众数。在所有同学合作的最优策略下,我们希望众数对应数字出现的次数尽可能多。请问出现次数最多的数字是多少呢?
输入格式
第一行,一个正整数 \(n\)。
接下来 \(n\) 行,其中第 \(i\) 行时非负整数 \(a_i,b_i\) 代表第 ii 名同学手上红卡和黑卡的数字。
输出格式
一个整数,表示答案。如果有多个解,请输出最小的那个。
输入
4
21 9
28 9
28 3
17 4
输出
21
说明/提示
样例解释:
众数出现次数最多是 \(3\) 次,有如下两种方法:
\(1\) 号同学直接出红卡,\(2\) 号同学出红黑异或,\(3\) 号同学随便出,\(4\) 号同学出红黑异或。这样 \(1,2,4\) 号同学都可以打出 \(21\)。
\(1\) 号同学出红黑异或,\(2\) 号同学直接出红卡,\(3\) 号同学直接出红卡,\(4\) 号同学随便出。这样 \(1,2,3\) 号同学都可以打出 \(28\)。
所以 \(21\) 和 \(28\) 都是出现次数最多的众数,因为最多可以出现 \(3\) 次,不存在出现 \(4\) 次的方案。但是由于要求如果有多解输出小的,请输出 \(21\)。
解题思路
hash
设置一个哈希表:hash:[x,y]
,表示x
在hash
中出现了y
次,这y
次是由不同同学贡献的,如果一个同学给的两个值相同,则对该值只贡献一次,要求的就是满足最大的y
的同时最小的x
- 时间复杂度:\(O(n)\)
代码
#include<bits/stdc++.h>
using namespace std;
unordered_map<int,int> mp;
int n,x,y;
int main()
{
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&x,&y);
mp[x]++;
if(y)
mp[x^y]++;
}
int mx=0,res=0;
for(auto [x,y]:mp)
if(mx<y)mx=y,res=x;
else if(mx==y&&res>x)res=x;
printf("%d",res);
return 0;
}
P6366 特殊的翻转
题目描述
k 老师在研究一段病毒程序的代码。这段代码是由一段长度不超过 \(10^6\) 的十六进制字符(也就是 0
到 9
和 A
到 F
)组成的信息。现在 k 老师要将其转换为二进制的 0/1 串(这个时候需要确保最高位是 1)。然后对这个 0/1 串进行“翻转”操作。
对于每次“翻转”操作,k 老师可以选择这个 0/1 串中的其中一位,将这一位和这一位相邻的两位,一共三位,分别进行“翻转”(也就是 0 变 1,1 变 0)。如果指定的这一位是序列的开头或者结尾,那么翻转这一位和存在的相邻位即可。
k 老师想知道,如何用最少的“翻转”步骤,将这个 0/1 串变为全 0 的串。
输入格式
一个十六进制的字符串,由 0
到 9
和 A
到 F
构成。
输出格式
最少能将其变为全 0 串需要的“翻转”步骤次数。如果无论如何都不能将其变为全 0 串,则输出 No
。
输入 #1
15
输出 #1
3
输入 #2
FF
输出 #2
3
输入 #3
10
输出 #3
No
说明/提示
样例解释:
十六进制的 15 对应二进制的 10101,翻转第 1/3/5 位,就可以全部变为 0。
十六进制的 FF 对应二进制的 11111111,翻转第 2/5/8 位,就可以全部变为 0。
十六进制的 10 对应二进制的 10000,无法全变为 0。
解题思路
枚举+贪心
首先,可以确定一点:同一个位置最多翻转一次,这是因为翻转两次等于没有翻转。从下标为 \(0\) 的位置开始,如果该位置为 \(1\),则有两种选择:翻转该位置或与其相邻的位置。且前面已经翻转好的,后面不应该对其有影响,依次递推即可;如果该位置为 \(0\),不考虑该位置。
- 时间复杂度:\(O(n)\)
代码
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
string he,bi;
string get(int x)
{
string t;
do
{
t+=to_string(x%2);
x/=2;
}while(x);
while(t.size()<4)t+='0';
reverse(t.begin(),t.end());
return t;
}
int op(int pos)
{
int res=0,n=bi.size();
if(bi.front()=='1')
{
res++;
if(pos==0)bi[1]=char(bi[1]^1),pos++;
else
{
bi[1]=char(bi[1]^1);
bi[2]=char(bi[2]^1);
}
}
for(int i=pos;i<n;i++)
{
if(bi[i]=='1')
{
if(i+1<n)
{
res++;
bi[i+1]=char(bi[i+1]^1);
if(i+2<n)bi[i+2]=char(bi[i+2]^1);
}
}
}
if(bi.back()=='1')return inf;
return res;
}
int main()
{
cin>>he;
for(char &c:he)
{
if(c>='0'&&c<='9')bi+=get(c-'0');
else
bi+=get(c-'A'+10);
}
while(bi.size()>1&&bi.front()=='0')bi.erase(bi.begin());
if(bi.size()==1)
{
if(bi[0]=='0')cout<<0;
else
cout<<1;
}
else if(bi.size()==2)
{
if(bi=="11")
cout<<1;
else
puts("No");
}
else
{
if(bi.front()=='1')
{
string t=bi;
int res1=op(0);
bi=t;
int res2=op(1);
if(res1==inf&&res2==inf)puts("No");
else
cout<<min(res1,res2);
}
else
{
int res=op(1);
if(res==inf)puts("No");
else
cout<<res;
}
}
return 0;
}