感谢大家今天来做题
比赛地址:http://202.206.177.79/contest/8
由于博主比较菜,没做完所有题目,这里暂时仅提供前两部分的题解。
为了节约篇幅,题目及数据描述不再赘述,如有需求,请移步OJ查看。
感谢大家的辛苦付出,但是从这次比赛的结果来看,前行之路还非常非常漫长呐。
趁机安利一个课程,比较适合大家学:北京理工大学ACM冬季培训课程
我寂寞的时候,会害怕踏出第一步。不会想到要去做什么事,所以,可能没有发觉很多很多的东西吧。——《夏目友人帐》
第一阶段
L1-1 天梯赛座位分配 (20分)
通过率:2.56%
(;;) 通过人数:1
模拟题,整体难度大于后面几道题。此外对输出格式要求极为严苛,比较坑。
比较优秀的特点是数据范围不大,所以可以直接用 (O(10*N*M)) 的时间模拟编号过程(细节见代码)。
在每个学校都还有余量时,相临两位选手的座位号是以 (N) 为公差的等差数列,
但一旦有一些学校满编了,我们要在模拟的过程中跳过该校(要注意下 (n) 的后继是 (1)),
如果检测到当前只有一所学校有余量,需要隔座编号,我的代码使用了 (add) 累计隔座数量。
细心的同学可以发现输出描述有一句:行首尾不得有多余空格
这就比较魔性了,一般的题目行尾是可以有空格的,所以输出时格外注意格式(我第一次交就没看到这个)。
代码如下:
#include <bits/stdc++.h>
#define MAXN 107
using namespace std;
int n,pos,add,tot,p[MAXN];
vector<int> v[MAXN];
int main() {
scanf("%d",&n);
for (int i=1;i<=n;i++) {
scanf("%d",&p[i]),p[i]*=10;
tot+=p[i];
}
for (int i=1;i<=tot;i++) {
int pre=pos;
pos=(pos==n)?1:pos+1;
while ((int)v[pos].size()==p[pos])
pos=(pos==n)?1:pos+1;
if (pre!=pos) v[pos].push_back(i); //正常编号
else v[pos].push_back(i+(++add)); //仅剩一所学校有余量,要隔座
}
for (int i=1;i<=n;i++) {
printf("#%d
",i);
for (int j=0;j<p[i];j++) {
printf("%d",v[i][j]);
putchar((j+1)%10?' ':'
'); //行尾不能有空格
}
}
return 0;
}
L1-2 倒数第N个字符串 (15分)
通过率:36.84%
(;;) 通过人数:14
模拟题,用C++写应该比较简单,我头铁用Python搞了半天。
模拟的过程和数学上的加减法的进位/借位比较像,以从倒着数为例:
zba
的前驱是 zaz
,模拟的操作就是从后往前找到第一个不为a
的字母,
然后把它之后的的所有a
(如果有的话)改成z
,计数器同时工作即可。
代码如下:
import math
def main():
l, n = input().split()
l = int(l); n = int(n)
ans = 'z' * l
for i in range(n-1):
pos = l - 1
while ans[pos]=='a': pos-=1
ans = ans[:pos] + chr(ord(ans[pos])-1) + (l-pos-1)*'z'
print(ans)
if __name__ == "__main__":
main()
L1-3 打折 (5分)
通过率:73.91%
(;;) 通过人数:34
考察了最简单的数据处理以及读入输出,注意一下整形到浮点形的运算语法以及保留小数即可。
代码如下:
def main():
a, b = input().split()
ans = float(a)*float(b)/10
print("%.2f" %ans)
if __name__ == "__main__":
main()
L1-4 2018我们要赢 (5分)
通过率:63.46%
(;;) 通过人数:33
这题通过率和通过人数还不如上一题,咋肥事啊。
直接输出!你敢送分我敢拿!
代码如下:
def main():
print("2018")
print("wo3 men2 yao4 ying2 !")
if __name__ == "__main__":
main()
L1-5 电子汪 (10分)
通过率:59.62%
(;;) 通过人数:31
考察了作为单身狗的口算水平,也没什么好说的。
代码如下:
def main():
a, b = input().split()
for i in range(int(a)+int(b)):
print("Wang!", end='')
if __name__ == "__main__":
main()
L1-6 福到了 (15分)
通过率:18.97%
(;;) 通过人数:11
模拟题,细节要求比较多。
题目所要求的倒置指的是整图旋转 (180°),满足的性质是 (map[i][j] -> map[n-i+1][n-j+1]),
简单观察一下样例可以知道,把列倒过来,再把行倒过来就倒置了。
判断是否输出bu yong dao le
可以使用上面那个性质,如果每一个坐标都满足就要输出。
代码如下:
def main():
k, n = input().split()
n = int(n)
l = []; r = []
for i in range(n):
opt = input().replace('@', k).ljust(n, ' ') #如果没有满的要用空格补全n
l.append(opt)
for i in range(n-1, -1, -1):
t = list(l[i]).reverse()
r.append(l[i][::-1])
fl = 0
for i in range(n):
if l[i] != r[i]:
fl = 1; break #发现有不满足性质的各自,标记一下不用输出
if fl == 0: print("bu yong dao le")
for i in range(n):
for j in range(len(r[i])):
if r[i][j]!=' ': print(k, end='')
else: print(' ', end='')
print('
', end='')
if __name__ == "__main__":
main()
L1-7 谁是赢家 (10分)
通过率:38.89%
(;;) 通过人数:28
考察了判断语句,观众票数和评委投票的权重不同,其中得某人到全部评委的认可是一个强条件,
在不符从该条件的情况下以观众票数较多者为优。
代码如下:
def main():
pa, pb = input().split()
pa = int(pa); pb = int(pb)
l = input().split()
va = 0
for i in range(3):
if l[i] == '0': va += 1
if va == 3 or (va > 0 and pa > pb):
print("The winner is a: %d + %d"%(pa, va))
else: print("The winner is b: %d + %d"%(pb, 3-va))
if __name__ == "__main__":
main()
L1-8 猜数字 (20分)
通过率:34.37%
(;;) 通过人数:22
根据题意,先要算平均数的一半,然后找出分数最接近这个值的选手。
(minn) 记录了当前最小的差值绝对值,(winter) 在线更新较优选手名字。
代码如下:
def main():
name = []; socre = []
n = int(input()); tot = 0
for i in range(n):
t1, t2 = input().split()
t2 = int(t2); tot += t2
name.append(t1); socre.append(t2)
average = int(float(tot/n)/2)
winter = ""; minn = 2147483647
for i in range(n):
if abs(socre[i] - average) < minn:
minn = abs(socre[i] - average)
winter = name[i]
print("%d %s"%(average, winter))
if __name__ == "__main__":
main()
第二阶段
L2-1 分而治之 (25分)
通过率:50.00%
(;;) 通过人数:2
简单的图论题,由于拆边比较麻烦,可以先离线记录每条边信息。
对于每个方案,用一个数组 (vis) 记录所打击的城市编号,之后遍历所有边,
只要有任一边合法(即两端城市都没有被打击),则该方案无效。
代码如下:
#include <bits/stdc++.h>
#define MAXN 10007
using namespace std;
struct Edge { int u,v; };
vector<Edge> E;
int n,m; bool vis[MAXN];
inline int readint() {
int w=0,X=0; char ch=0;
while(!isdigit(ch)) w=ch=='-',ch=getchar();
while (isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
inline bool judge() {
memset(vis,0,sizeof(vis));
//vis数组标记打击的城市编号
int Np=readint();
for (int i=1;i<=Np;i++) vis[readint()]=true;
for (int i=0;i<m;i++)
if (!vis[E[i].u] && !vis[E[i].v]) //如果某条路两端都没有被打击,则方案无效
return false;
return true;
}
int main() {
n=readint(),m=readint();
for (int i=1;i<=m;i++)
E.push_back(Edge{readint(),readint()});
int k=readint();
while(k--) puts(judge()?"YES":"NO");
return 0;
}
扩展阅读:
L2-2 小字辈 (25分)
通过率:66.67%
(;;) 通过人数:2
考察了图的存储和遍历,我这里使用的是 (vector) 存图,推荐大家也使用这种。
为了方便数据处理(数组下标需小于等于 (0),老祖宗的父节点下标为 (-1)),我把所有下标暂时 (+1),输出时再 (-1)。
在读入的过程中,从父节点向子节点连一条边,完成建图。
(dfs()) 函数的作用是依次给每个节点附上辈分值,顺便求出最小的辈分。
代码如下:
#include <bits/stdc++.h>
#define MAXN 100007
using namespace std;
int n,book,dep[MAXN];
vector<int> son[MAXN];
inline int readint() {
int w=0,X=0; char ch=0;
while (!isdigit(ch)) w=ch=='-',ch=getchar();
while (isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
void dfs(int u) { //图的深度遍历
book=max(book,dep[u]);
for (int i=0;i<(int)son[u].size();i++) {
dep[son[u][i]]=dep[u]+1;
dfs(son[u][i]);
}
}
int main() {
n=readint();
for (int i=1;i<=n;i++) {
int u=readint();
son[u+1].push_back(i+1);
}
dep[0]=0,dfs(0);
printf("%d
",book);
for (int i=1;i<=n;i++)
if (dep[i+1]==book) printf("%d ",i);
return 0;
}
扩展阅读:
L2-3 名人堂与代金券 (25分)
通过率:50.00%
(;;) 通过人数:1
一道看起来不难的模拟题,实际上细节要求非常多,要把各种情况想到位。
首先推荐使用一个结构体存储每个学生的信息,然后按分数从大到小排序,如有同分,则按照题意以账号字母升序排。
对于第一个问题,即求发放代金卷的总面值,直接累加后输出即可。
对于输出排名,要注意同分者排名是并列的,我们使用 (cnt) 记录真实排名,一旦发现与前人分数不同,则用 (i) 矫正。
还有一种方法是给结构体赋排名值,这样应该写起来更简单些,但思路差不多。
代码如下:
#include <bits/stdc++.h>
#define MAXN 100007
using namespace std;
struct student {
string id; int soc;
bool operator < (const student &T) {
return (soc>T.soc)||(soc==T.soc && id<T.id);
//两个排序关键字,优先以分数排序,分数相同时以账号排升序
}
}s[MAXN];
int n,g,k,cnt=0,sum=0;
int main() {
cin>>n>>g>>k;
for (int i=1;i<=n;i++) {
cin>>s[i].id>>s[i].soc;
if (s[i].soc>=g) sum+=50;
else if (s[i].soc>=60) sum+=20;
}
cout<<sum<<'
'; sort(s+1,s+n+1);
for (int i=1;i<=k;i++) {
if (s[i].soc!=s[i-1].soc) cnt=i; //cnt控制真实名次
cout<<cnt<<' '<<s[i].id<<' '<<s[i].soc<<'
';
}
for (int i=k+1;i<=n;i++) {
if (s[i].soc!=s[i-1].soc) break;
cout<<k<<' '<<s[i].id<<' '<<s[i].soc<<'
';
}
return 0;
}
L2-4 秀恩爱分得快 (25分)
通过率:33.33%
(;;) 通过人数:1
下面的代码有点长,其实写的时候很多内容是可以 Ctrl + V
的。
仍是一道模拟题,题目中的关键要素有:亲密度、亲密值、性别。
首先观察到用尽管性别用正负数区分,但是其绝对值是没有重复的,所以考虑用单独一个数组 (sex) 记录性别,然后可以将所有编号全部取绝对值。
由于只关心两位主人公对于其他的亲密值,所以可以先记录下照片信息,然后离线统计,这样就只需要两个一维数组就可以保存我们所需的所有信息。
在离线读取每张照片的信息时,使用 (fa) 和 (fb) 标记两位主人公是否有出现,如有出现,则按题目所给公式累计对其余人的亲密值。
不定数组 (la) 和 (lb) 记录的就是当前两人亲密值最大的人的集合,保证对集合内容的人亲密值相等。
注意如果当前集合内只有对方,访问到一个亲密值相同的他人,则不予加入。
还有注意的是性别问题,同性可以忽略,异性才进行计算,具体细节请见代码。
代码如下:
#include <bits/stdc++.h>
#define MAXN 1007
#define eps 1e-7
using namespace std;
int n,m,a,b,k[MAXN],sex[MAXN];
double va[MAXN],vb[MAXN];
vector<int> p[MAXN],la,lb;
inline int readint() {
int w=0,X=0; char ch=0;
while (!isdigit(ch)) w=ch=='-',ch=getchar();
while (isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
int main() {
n=readint(),m=readint();
for (int i=1;i<=m;i++) {
k[i]=readint();
for (int j=0;j<k[i];j++) {
int opt=readint();
if (opt>=0) sex[opt]=1,p[i].push_back(opt);
else sex[-opt]=-1,p[i].push_back(-opt); //取编号绝对值
}
}
a=abs(readint()),b=abs(readint()); //取编号绝对值
for (int i=1;i<=m;i++) {
bool fa=false,fb=false;
for (int j=0;j<k[i];j++) {
if (p[i][j]==a) fa=true;
if (p[i][j]==b) fb=true;
}
for (int j=0;j<k[i];j++) {
if (fa && p[i][j]!=a) va[p[i][j]]+=1.0/k[i]; //按照题目给出的公式计算亲密度
if (fb && p[i][j]!=b) vb[p[i][j]]+=1.0/k[i];
}
}
la.push_back(b),lb.push_back(a);
for (int i=0;i<n;i++) {
if (va[i]>va[la[0]]+eps && sex[a]*sex[i]<0) //eps控制浮点误差,sex确保性别
la.clear(),la.push_back(i); //发现亲密值大于集合内的异性,先清空,后加入新人
else if (abs(va[i]-va[la[0]])<eps && sex[a]*sex[i]<0 && va[la[0]]>va[b]+eps)
la.push_back(i); //发现一样的且集合内没有原配,则直接加入
if (vb[i]>vb[lb[0]]+eps && sex[b]*sex[i]<0)
lb.clear(),lb.push_back(i);
else if (abs(vb[i]-vb[lb[0]])<eps && sex[b]*sex[i]<0 && vb[lb[0]]>vb[a]+eps)
lb.push_back(i);
}
if (la[0]==b && lb[0]==a) printf("%d %d",sex[a]*a,sex[b]*b); //简单输出控制
else {
for (int i=0;i<(int)la.size();i++) printf("%d %d
",sex[a]*a,-sex[a]*la[i]);
for (int i=0;i<(int)lb.size();i++) printf("%d %d
",sex[b]*b,-sex[b]*lb[i]);
}
return 0;
}
以上。