2016中山市邀请赛
Posted John_pascal
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2016中山市邀请赛相关的知识,希望对你有一定的参考价值。
前言:
这次考的极差,竟然连第一题都做错,真的是脑子短路。不过失败乃成功之母,吸取教训,争取下次考好。60+20+100+100+0——呵呵第一题:
小明今天生日,邀请了一些朋友过来开生日会。妈妈专门去买了一个大蛋糕,蛋糕为一个n*m的矩形,现在想把这个蛋糕分成1*2的小块,并且要求必须是完整的小块,不能拼接。问一共能分多少块? Input一行,两个正整数n,m
Output
一行,一个整数x,表示最多能分多少块
Sample Input 7 8 Sample Output 28
HINT
50% 数据 0<n,m<10000
100% 数据 0<n,m<10^9
这道题目真的是a+b一样简单,但是我竟然能做错?想象不到吧,不是int64的问题(对于一个小牛,这个范围的问题还是知道的),那我错在什么地方?错在我看题不想题,说不能剩余,我就以为是n*(m div 2),傻叉!还可以横着啊!真傻!正解是n*m div 2.(我已无言以对) 代码:
var
a,b:int64;
begin
readln(a,b);
writeln(a*b div 2);
end.
第二题:
kqp发明了一个好玩的游戏,叫czy一起玩。但czy玩了十几盘,总是输,他想知道是不是从一开始他就注定要输。这个游戏是这样的,kqp先写下一排数(既然是一排,当然有首尾咯)。kqp和czy每次只能从这排数的头或尾取一个数。最后谁取的数的和多,谁就赢了。如果两人的数的总和一样多,先取者胜。有天FW看到他们俩在玩这个游戏,很好奇。他想知道,在两人总是做出最优决策的情况下(两个人的智商都是很高的……),谁能取得最终的胜利呢?Input
第一行为一个数k(k<=10),表示有k组测试数据; 以下k组测试数据:
每组测试数据中,第一行仅有一个偶数n(0<n<=100000),
第二行也仅有一个数,0表示kqp先取数,1表示czy先取数
第三行有n个数,是kqp给出的一排数。这n个数的绝对值均不超过106。
Output
对每组测试数据输出一行
表示在两人总是做出最优决策的情况下,
最终的胜利者的名字,即"kqp"或"czy"(引号不输出)。
Sample Input
2
1
1 3
2
0
1 3
Sample Output
kqp
HINT 30%,k=1,n<=10; 100%,如题所述。
这道题就是 一道“坑的不能再坑”的题目,所谓的“最优策略”应该指的是一种动态规划的决策,也就是常说的DP,但是这道题的最优策略指的是一种贪心的取法。做过usaco a game那道题的同学都知道,那道题就是用DP,而且还属于较难的动态规划。但考试时的第2题是不可能出现这种题目的,所以从这里我们应该可以推断出是用贪心。我考试时候就用了贪心,但是为什么还错?因为这是一道“坑的不能再坑的”题目,例如当前1,n两个位置选,如果先选的人取了1,则后选的人只能选n,每次只能取头和尾,不是先取的人取了1之后,后取的可以取2,这是不一样的,所以这题有多简单?就是输出先取的人的名字即可。什么都不用模拟。 代码:
var
k,n,i,j,x,y:longint;
begin
readln(k);
for i:=1 to k do
begin
readln(n);
readln(x);
for j:=1 to n do
read(y);
if x=0 then writeln('kqp') else writeln('czy');
end;
end.
第三题:
最近备受关注的人机大战——谷歌机器人AlphaGo对战围棋大师李世石。经过五盘的对决,最终AlphaGo以4:1战胜李世石,并且使得它的排名一举上升为世界第二,仅次于中国选手柯洁。为了准备迎接柯洁的挑战,必须让AlphaGo提升自身的处理能力,但由于时间有限,仅能临时采购一些性能不一的处理器,现在知道每种处理器的处理能力和发热量,由于机器过热可能会导致AlphaGo程序崩溃,必须要控制好它的最大发热量才行,这个艰巨的任务落在你的头上,必须选出一些处理器来尽可能的提供最强的处理能力。
input
第一行两个正整数n,t,表示可选择的处理器种类和最大发热量,注意,每种处理器可以采购多个,
接下来n行,每行两个正整数,分别表示每种处理器的处理能力和发热量(数值均小于100)
output
一行,一个正整数,表示AlphaGo的最大处理能力
Sample Input
3 5
2 2
4 3
1 5
Sample Output
6
HINT
50% 数据 n<=30
100% 数据 n<=300,t<=10000
这道题目其实就是一个背包问题,因为每个处理器可以采购多个,所以是完全背包问题,在这里不多讲,不会的去翻翻资料吧。
代码:
var
f:array[0..10000] of Longint;
w,v:array[1..300] of Longint;
n,t,i,j:longint;
begin
readln(n,t);
for i:=1 to n do
readln(w[i],v[i]);
for i:=1 to n do
for j:=v[i] to t do
if f[j-v[i]]+w[i]>f[j] then f[j]:=f[j-v[i]]+w[i];
writeln(f[t]);
end.
第四题:
小明的数学计算能力超强,常常在同学们面前表面得很骄傲。数学科代表实在看不下去了,决定出道很麻烦的题,好好“折磨”他一下。数学科代表决定给他一些数,让他分组。从第一个数开始分组,且每组必须是连续的一段数,要求每组和相等,问每组和最小可以是多少。(当然这些数一定可以被分组,大不了直接分成一组。)
input
第一行为一个数N
第二行为N个整数(每个数均小于等于1000),两个数间用空格隔开。
Output
一行,最小的和
Sample Input 6
2 5 1 3 3 7 Sample Output 7
HINT
分成三组(2,5) (1,3,3) (7) 和为7,不存在比7更小的和。
测试点 | n |
1 | n = 10 |
2 | n = 100 |
3 | n = 1000 |
4 | n = 200000 |
5 | n = 200000 |
6 | n = 1000000 |
7 | n = 1000000 |
8 | n = 1000000 |
9 | n = 1000000 |
10 | n = 1000000 |
这道题目我在考场上想的是,先枚举分成多少组,当然是从多至少(分的组越多和就越小)所以,当我枚举完有多少组之后我就开始二分了,如何二分?先求一个前缀和,例如 a =[2 5 1 3 3 7] sum=[2 7 8 11 14 21]
s:=k/i s表示的是当前分成i组,每一组的和。 然后用s看在sum数组里面的哪里,如果能找到,就继续判断下一组是否可以找到。 代码:
var
flag:boolean;
f:array[0..1000000] of Longint;
a:array[1..1000000] of Longint;
n,i,j,k,sum,l,r,mid:longint;
begin
readln(n);
for i:=1 to n do
begin
read(a[i]);
inc(sum,a[i]);
f[i]:=f[i-1]+a[i];
end;
for i:=n downto 1 do
begin
if sum mod i<>0 then continue; //这里同样也可以加一个与下面方法相同的优化。
k:=sum div i;
flag:=true;
l:=1;
for j:=1 to i do //依次判断i组是否可以组成.
begin
r:=n;
while l<=r do
begin
mid:=(l+r) div 2;
if f[mid]=k then break;
if f[mid]>=k then r:=mid-1 else l:=mid+1;
end; //二分是找当前这一组的和是否能组成.
if f[mid]<>k then
begin
flag:=false;
break;
end;
inc(k,sum div i); //能组成就下一组,同时更新要找的数以及上限l.
l:=mid+1;
end;
if flag then break; //如果这i组都可以组成则直接break.
end;
if flag then writeln(sum div i) else writeln(sum);
end.
但是这样子做,时间是很紧的,接近超时,考试能AC考的是大大的rp,但其实还有一种方法:
可以不用前缀和+二分,直接模拟出他累加的和就行了,如果累加的时候大于s则break。但是要加一个小优化,就是如果当前s都小于a数组的最大值了,就没必要模拟下去。
代码:
var
i,j,n,tot,sum,s,max:longint;
a:array[0..1000000] of longint;
begin
readln(n);
for i:=1 to n do
begin
read(a[i]);
inc(sum,a[i]);
if a[i]>max then max:=a[i];
end;
for i:=n downto 1 do
begin
if (sum mod i<>0) or (sum div i<max) then continue; //这里就是优化和pd
s:=sum div i;
tot:=0;
for j:=1 to n do
begin
inc(tot,a[j]);
if tot>s then break;
if tot=s then tot:=0;
end; //这里直接模拟.
if tot=0 then break;
end;
if tot=0 then writeln(s) else writeln(sum);
end.
第五题:
兰姐姐是来自火星的女王。相信你们一定对兰姐姐不熟悉,她统领整个火星,在各方面拥有最高权力。很久很久以前,兰爸爸是火星的国王,去世以后,两个女儿争夺王位。火星上最聪明的人是辣椒酱,他帮助兰姐姐夺得了王位,而兰姐姐的姐姐Horse没有得到王位,便离开火星前往地球修行。几年后,兰姐姐越来越思念姐姐,便决定到地球上找姐姐。今天,她找到了自己失散已久的姐姐Horse的家,但是要进门就必须答对一个大难题,作为一个大犇犇犇,她很快就解出来了,你行吗?
题目是这样的:现在有一个序列a,a的长度为n,一开始a[i]=i(1≤i≤n),现在有m个操作,每个操作的格式是这样的:x y表示把当前的a[x]与a[y]交换。我们把这m个操作叫做一轮操作,现在问,在经过多少轮操作之后,序列a又会回到原来的样子(原来的样子就是指a[i]=i(1≤i≤n))
Input
第一行,两个整数n,m,n表示a的长度,m表示操作数
接下来m行,每行一个操作x y,表示把当前的ax与ay交换保证(1≤x,y≤n)
Output
只有一个数,表示在经过多少轮之后,序列a又会回到原来的样子
Sample Input
4 4
1 4
3 4
2 3
1 4
Sample Output
3
HINT
50%数据保证1≤n,m≤1000,答案小于等于1000
100%数据保证1≤n,m≤500000,答案小于等于2^31-1
这道题目考试时没捞到1分,就是因为读题不仔细,当然也有一部分题目描述不完整的情况,但是,为什么其他人能明白,归根到底还是自身的问题,已经说了是多少轮操作之后才会得到原来的序列,一轮操作——必定是完整的一轮,不然怎么叫一轮操作,那就叫一些操作。
所以这题用模拟的方法可以拿到50分,但是我们可以进一步优化。
我们先用数据来模拟交换的一轮:
1 2 3 4
4 2 3 1
4 2 1 3
4 1 2 3
3 1 2 4
我们可以留下一轮繁琐操作的本质,也就是1个位置换来换去,到底换到了哪儿。
f[1]=1,2
f[2]=2,3
f[3]=3,1
f[4]=4,4
f[i]表示的是第i个位置一轮操作之后换到了f[i]这个位置。
我们可以发现4他是不换的,所以4要回到原来的位置只用1次。而1,2,3可以发现他们是组成了一个循环
1-2-3-1,所以1,2,3回到原来的位置只用3次,他们的最小公倍数gbs(1,3)=3,所以答案=3
再举一个栗子:
5 3
1 2
2 3
4 5
交换一轮:
1 2 3 4 5
2 1 3 4 5
2 3 1 4 5
2 3 1 5 4
f[1]=1,3
f[2]=2,1
f[3]=3,2
f[4]=4,5
f[5]=5,4
1,2,3组成了一个循环1-3-2-1
4,5组成了一个循环4-5-4
1,2,3用3次,4,5用2次,gbs(3,2)=6
从这里我们就可以发现,其实我们只要看f数组组成了多少个循环,然后求出n个循环个数的公倍数即可。
代码:
type
arr=array[1..500000] of int64;
var
a:arr;
f:Array[1..500000,0..1] of longint;
x,y:Array[1..500000] of Longint;
bz:array[1..500000] of boolean;
n,m,ans,answer,t,tot:int64;
i,j:Longint;
function gcd(x,y:int64):int64; //求出gbs——公倍数.
var
z,xx,yy:int64;
begin
xx:=x; yy:=y;
while x mod y<>0 do
begin
z:=x mod y;
x:=y;
y:=z;
end;
exit(xx*yy div y);
end;
procedure sort(l,r:Longint); //快排用于方便模拟.
var
i,j,mid,p:longint;
begin
i:=l; j:=r;
mid:=f[(i+j) div 2,0];
while i<j do
begin
while f[i,0]<mid do inc(i);
while f[j,0]>mid do dec(j);
if i<=j then
begin
p:=f[i,0]; f[i,0]:=f[j,0]; f[j,0]:=p;
p:=f[i,1]; f[i,1]:=f[j,1]; f[j,1]:=p;
inc(i); dec(j);
end;
end;
if l<j then sort(l,j);
if i<r then sort(i,r);
end;
begin
readln(n,m);
for i:=1 to m do readln(x[i],y[i]);
for i:=1 to n do a[i]:=i;
for i:=1 to m do
begin
t:=a[x[i]]; a[x[i]]:=a[y[i]]; a[y[i]]:=t; //交换一轮后的根本.
end;
for i:=1 to n do
begin
f[i,0]:=a[i];
f[i,1]:=i;
end; //判断出第i个位置到底是换到了哪里,表示为f[i,1].
sort(1,n);
fillchar(bz,sizeof(bz),true);
answer:=1;
for i:=1 to n do
if bz[i] then //优化——已经有在循环里的数,无需再次判断。
begin
j:=f[i,1];
ans:=1;
while j<>i do //while 循环用于找当前第i个数是在哪个循环,ans用来记录这个循环每个数要回到自己位置的个数.
begin
bz[j]:=false;
j:=f[j,1];
inc(ans);
end;
answer:=gcd(answer,ans);
end;
writeln(answer);
end.
小结:
这次考试考得不好,但收获才是最重要的,通过这次比赛,我明白了思维不要局限于1面,越广越好,像第1题犯得这种错误,以后不能再犯,还有从这次比赛结束后,我需要对自己进行几点要求:
1、做题时要快。根据题目的难度与实现的时间应该成正比,难的题目要能快速想到方法,简单的题目要能快速实现。
2、保证少提交次数。也就是在最少的次数AC一道题目,因为在考试时只有一次提交的机会,平时做题时如果不严谨,做的马马虎虎就提交,也不看优化数据之类的,考试时就惨了。
3、这次比赛还是没有做到心无杂念,总想着要拿第一或者怎样,但是这样子往往会影响我的正常发挥,希望下次能不要想太多,仔仔细细的做每一道题就好了, 名次无谓。
以上是关于2016中山市邀请赛的主要内容,如果未能解决你的问题,请参考以下文章