2016中山市邀请赛

来源:互联网 发布:算法设计与分析第四版 编辑:程序博客网 时间:2024/04/29 03:06

前言:

这次考的极差,竟然连第一题都做错,真的是脑子短路。不过失败乃成功之母,吸取教训,争取下次考好。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、这次比赛还是没有做到心无杂念,总想着要拿第一或者怎样,但是这样子往往会影响我的正常发挥,希望下次能不要想太多,仔仔细细的做每一道题就好了, 名次无谓。
0 0
原创粉丝点击