文章索引: 这一篇博客,算是我的作业吧。 根据难度与时间,我从完善程序的第一题开始,从NOIP2009到NOIP2015年,逐题写出思考思想。 NOIP2009(完善程序第一题):最大连续字段和 题意描述: 给出一个数列(元素个数不多于100),数列元素均为负整数、正整数、0。请找出数列中的一个连续子数列,使得这个子数列中包含的所有元素之和最大,在和最大的前提下还要求该子数列包含的元素个数最多,并输出这个最大和以及该连续子数列中元素的个数。 题目源代码: 解题过程:
空<1>其实很明显,前面都在初始化,这个beg一起初始化就好了,即beg=0. 空<2>之后的表达式依然将长度扩充,得到现统计结果(tmp+a[i])与原统计结果(ans)相同。即tmp+a[i]==ans 空<3>发现之后的表达式中将目前累计的结果(tmp)清空,题意中提到“所有的元素均为正整数、负整数、0”,所以有出现0的情况,如果最大连续字段和中出现负数的情况下一定不是最优解,所以这个空是特判和小于0的,即 tmp+a[i]<0(实际上填写“<0”) 空<4>显而易见,既然将beg再一次进行赋值,因为从原来到现在的总和出现负数,所以将beg重新设置成i就可以了。即 beg = i(实际上填写"i") 空<5>就是在tmp+a[i]大于等于0的时候,将计数器累加即可。即tmp+=a[i](或者tmp=tmp+a[i]) (这道题目居然不是用状态转移方程来写,丧病啊!) 下一题。 NOIP2010(完善程序第一题):哥德巴赫猜想 题意描述: 哥德巴赫猜想是指,任一大于2的偶数都可写成两个质数之和。迄今为止,这仍然是一个著名的世界难题,被誉为数学王冠上的明珠。试编写程序,验证任一大于2且不超过n的偶数都能写成两个质数之和。 题目源代码: 如果输入2010,输出的是 空<5> 解题过程 空<2>得知p数组为素数表,那么每一次搜索时扫描素数表中的每一个元素。如果扫描到了该元素的倍数,则标记。 所以填i%p[j]==0(实际上填"p[j]") 空<3>如果找到一个素数,就把r扩展,将得到的新元素(i)赋值给目前为空的p[r]中。即p[r] = i 空<4>哥德巴赫猜想:任一大于2的偶数都可写成两个质数之和。如果i的两倍与任意两个素数和相同,就将tmp设成true,得到这里需要枚举素数。所以写成if(i+i==p[j]+p[k])(实际填写“p[j]+p[k]”)。 空<5>这。。。这不是阅读程序写运行结果么?? 不解释。答案1004 好吧=_=经历了不知道为什么 NOIP2011(完善程序第一题):子矩阵 题意描述: 给输入一个n1*m1的矩阵a,和n2*m2的矩阵b,问a中是否存在子矩阵和b相等。若存在,输出所有子矩阵左上角的坐标:若不存在输出“There is no answer”。 题目源代码: 这一道题......
当我打出这道题目的代码之后,我无风自凌乱=_=,我只想说: 这已经不能叫水题了,简直是送分!送分啊! 空<1>输入...... 答案:cin>>b[i][j] 空<2>直接照搬上面的for循环。 答案:m1-m2+1; 空<3>因为good没有初始化,下面又将它设置为false,得到这里将good设置为true。即good = true 空<4>照搬...... 答案:m2 空<5>......我说什么好呢......因为得到答案,将haveAns设为true。答案:haveAns = true 下一题..... NOIP2012(完善程序第一题):坐标统计 题意描述: 输入n个整点在平面上的坐标。对于每个点,可以控制所有位于它左下方的点(即x、y坐标都比它小),它可以控制的点的数目称为“战斗力”。依次输出每个点的战斗力,最后输出战斗力最高的点的编号(如果若干个点的战斗力并列最高,输出其中最大的编号)。 题目源代码:
思路描述: 空<1>:为了防止上一次的统计结果影响到这一次,所以要清0.即f[i] = 0(实际上填写“0”) 空<2>:因为需要X2,Y2坐标都需要小于X1,Y1时,点1才能拥有点2的战斗力+1,所以填写y[j]<y[i] 空<3>:因为控制了当前点,战斗力增加。填写f[i]++或f[i] = f[i]+1 空<4>:...... 空<5>:因为如果有多个点战斗力相同,就要输出编号最大的点的编号,所以需要记住当前点的编号,继续向后(有可能有更大的编号)。又因为输出的是ans,得到答案:ans = max_f。 (令人欣慰的是:CCF在2012世界末日的时候没有放弃治疗) NOIP2013(完善程序第一题):序列重排 题意描述: 全局数组变量a定义如下: 它记录着一个长度为n的序列a[1], a[2], …, a[n]。现在需要一个函数,以整数p (1 ≤ p ≤ n)为参数,实现如下功能:将序列a的前p个数与后n-p个数对调,且不改变这p个数(或n-p个数)之间的相对位置。 题目源代码:
开始解题√ 这里我们需要画图了。 设: a[ ] = {1,2,3,4,5} , p=2 初始序列: 空<1>:很明显这里是出来p之前(包括p)的元素,将这里的元素进行替换。我们的目标数组是:
经过简单演算后得知:b[n-p+1] = a[i],即正确答案为b[n-p+1]。 空<2>:同样依照上面的图片。 元素位置转移: 3→1 , 4→2 , 5→3 经过演算后得知:b[i] = a[i+p],即b[i-p] = a[i],答案是a[i]。 空<3>:虽然不知道他为什么要覆盖a数组(可能是b数组还要用),总之就是讲b数组复制到a。所以填写n NOIP2014(完善程序第一题):数字删除 题意描述: 给定一个字符串,将字符串中的数字字符删除后输出。 题目源代码:
解题开始:
空<1>:很明显,这里是要判断这个字符是否是数字,即 s[i]<‘0’||s[i]>‘9’;(实际上填写"||") 空<2>:因为要统计更新后数组的长度,将j记为数组长度,这里更新了数组,所以长度加1,即j++ 空<3>:计算完毕,返回的是数组长度,即j 空<4>:输出更新后的s数组,即cout<<s[i](实际填写"s[i]") NOIP2015(完善程序第一题):打印月历 题意描述: 输入月份m(1 ≤ m ≤ 12),按一定格式打印2015年第m月的月历。 题目源代码: 解题思路:
2015年1月月历奉上 空<1>:offset这个参数控制着每个月前输出空格的数量。首次设成4.(上面的日历,如果再将周日放到周一前面,就有4个空),即offset=4. 空<2>:这里是为了得到每一个月输出日历前面输出的空格数量。这里有一个神奇的规律。将offset的初始值设置为4后,打印过当月的月历之后,留下的空就是(offset+当月天数)%7,即(offset+Daynum[i])%7。 空<3>:因为开始打印月历,所以要循环第m月的天数。即for(i=1;i<Daynum[m];i++)。(实际上填写"Daynum[m]") 空<4>:打印出i。即cout<<i(实际上填写"i") 空<5>:这里是控制每一星期后的换行。那么每一次打印的日期加上offset若取余7等于0,就可以换行。即(offset+i)%7==0。(实际填写"offset+i") 第一题全部解题完毕。 ============================ 接下来对第二题进行解答。 NOIP2009(完善程序第二题):国王放置 题意描述: 在n*m的棋盘上放置k个国王,要求k个国王互相不攻击,有多少种不同的放置方法。假设国王放置在第(x,y)格,国王的攻击的区域是: (x-1,y-1), (x-1,y),(x-1,y+1),(x,y-1),(x,y+1),(x+1,y-1),(x+1,y),(x+1,y+1) (注:棋盘行标号为0→n-1,列标号为0→m-1) 题目源代码: 解题思路:
空<1>:因为y搜索到了最右边,所以将y清零,x继续搜索下去。 空<2>:因为找到了一个可行的解,所以更新hash。即hash[i][j]++; 空<3>:这里进行递归。因为找到可行放置方法,所以递归的时候,返回的是更新的x,y,以及tot++。即work(x,y,tot+1); 空<4>递归完成,不能结束递归。所以将hash重新置为0.这里是hash[i][j]--; 空<5>开始递归。即work(0,0,0)。(因为棋盘的横坐标标号为0~n-1,纵坐标标号为0~m-1,所以要从0,0开始更新) NOIP2010(完善程序第二题):过河问题 题意描述: 在一个月黑风高的夜晚,有一群人在河的右岸,想通过唯一的一根独木桥走到河的左岸.在伸手不见五指的黑夜里,过桥时必须借照灯光来照明,不幸的是,他们只有一盏灯.另外,独木桥上最多能承受两个人同时经过,否则将会坍塌.每个人单独过独木桥都需要一定的时间,不同的人要的时间可能不同.两个人一起过独木桥时,由于只有一盏灯,所以需要的时间是较慢的那个人单独过桥所花费的时间.现在输入N(2<=N<1000)和这N个人单独过桥需要的时间,请计算总共最少需要多少时间,他们才能全部到达河左岸. 题目源代码:
解题思路:
空<1>:这个空是一次特判。如果人数只有两个人或者更少,那么不需要继续运算,返回答案即可。即n<=2 空<2>:这里是人从右向左走的时间判定操作。如果有两个人向左走,那么就把这两个人设成LEFT,表示他们目前在左边。那么他们前进的时间就是两个人中用时多的那个人的用时,但是因为过桥需要灯,所以要有人重新回到右边。这里要用到递归,执行向右走操作。即go(RIGHT_TO_LEFT) 空<3>:这里是判定当前选中的人i是否在左边。如果在,那么就继续执行送灯操作。即if(pos[i] == LEFT) 空<4>:这里是更新tmp,要用当前这个在左边的人去送灯,所以消耗时间是当前这个人的时间,即hour[i],然后需要进行左走右的操作,即go(LEFT_TO_RIGHT),那么tmp就是hour[i]+go(LEFT_TO_RIGHT) 空<5>这个人最后需要回到左边,所以在递归结束后将pos[i]设置成LEFT,即pos[i] = LEFT NOIP2011(完善程序第二题):大整数开方 题意描述:输入一个大整数,用二分法求出这个数的平方根的整数部分
空<1>:因为是计算两数的乘积,所以爆保存到第i+j-1位中。即ans.num[i+j-1] 空<2>:这里是进位操作。所以要将这个数对10取余。即ans.num[i]%=10 空<3>:这里是加法操作,将a与b的num[i]相加即可。即a.num[i]+b.num[i] 空<4>:因为要求平局数,那么就要让这个数取余2,,即ans.num[i]%2 空<5>:因为在更高位存在数字,那么将这个数字的长度加1即可。即ans.len++ 空<6>:如果b比a长(这样值更大),就返回false,即a.len<b.len
NOIP2012(完善程序第二题):排列数 题意描述:输入两个正整数n,m(1<n<20,1<m<n),在1~n中任取m个数,按字典序从小到大输出所有这样的排列。 题目源代码:
解题咯~ 空<1>:因为flag是一个记录变量,所以要一开始初始化为false。答案为false 空<2>:used代表着“使用过”,那么这里的data[i]没有使用过,所以将data[i]设置为没有使用过。即used[data[i]]=false 空<3>:如果j没有使用过,就将j的值记入。即data[i]=j(实际填写“j”) 空<4>:因为要从n个数里选m个,所以这里要枚举这n个数。即j<=n(实际填写"n") 空<5>:因为做完了,所以退出循环。即break 接下来。 NOIP2013:二叉查找树 题意描述:二叉查找树具有如下性质:每个节点的值都大于其左子树上所有节点的 值、小于其右子树上所有节点的值。试判断一棵树是否为二叉查找树。 题目源代码:
空<1>:如果当前节点的值不存在(为0), 那么不能进行操作。所以要保证cur大于0.所以填写"cur" 空<2>:因为已经遍历完左子树,所以开始遍历右子树(设置为根)。填写a[root].right_child 空<3>:这里将最低的值设置成cur,因为要保证下一个数(在遍历右子树)大于这个数。 空<4>:这里的最大值不需要改变(因为是遍历右子树),所以依旧是upper_bound 空<5>:这里要从根节点开始。即1。 NOIP2014:最大子矩阵和 题意简述:给出m行n列的整数矩阵,求最大的子矩阵和(子矩阵不能为空)。 题目源代码: 空<1>:ans的最大答案目前是子矩阵1行1列。所以设置为matrix[1][1]
空<2>:这里是初始化操作,初始化rowsum。因为rowsum是用来统计前i行第j个数的和,所以初始化时就是rowsum[i][0]=0;(前i行第0个数) 空<3>:这里是前i行第j个数的和。所以是前i-1行和加上当前数。当前数即matrix[i][j]。即rownum[i-1][j]+matrix[i][j] 空<4>:因为area没有赋值。所以是初始化area,即area=0 空<5>:这里要加上当前行的last和first-1两个数。即rownum[i][last]+rownum[i][first-1] NOIP2015:中位数 题意描述:给定n(n为奇数且小于1000)个整数,整数的范围在0~m(0 < m < 231) 题目源代码:
空<1>:因为是二分法,所以必须要保证左小于右。所以就是lbound<=rbound 空<2>:这里要将计数器count置为0(根据下面的count>n/2得知).即count=0 空<3>:这里其实要判断有多少数比mid大(根据下面的if(count>n/2)得知),那么这里就是填写mid<x[i] 空<4>:计数。即count++; 空<5>:属于二分的最后操作,将右赋值为mid-1。即rbould=mid-1。 好吧。。。。终于做完了。。。 |
|