NOIP初赛试题分类汇总完善程序部分.docx
- 文档编号:9800291
- 上传时间:2023-02-06
- 格式:DOCX
- 页数:33
- 大小:69.35KB
NOIP初赛试题分类汇总完善程序部分.docx
《NOIP初赛试题分类汇总完善程序部分.docx》由会员分享,可在线阅读,更多相关《NOIP初赛试题分类汇总完善程序部分.docx(33页珍藏版)》请在冰豆网上搜索。
NOIP初赛试题分类汇总完善程序部分
NOIP初赛试题汇总
完善程序
'2004
1.Joseph
题目描述:
原始的Joseph问题的描述如下:
有n个人围坐在一个圆桌周围,把这n个人依次编号为1,…,n。
从编号是1的人开始报数,数到第m个人出列,然后从出列的下一个人重新开始报数,数到第m个人又出列,…,如此反复直到所有的人全部出列为止。
比如当n=6,m=5的时候,出列的顺序依次是5,4,6,2,3,1。
现在的问题是:
假设有k个好人和k个坏人。
好人的编号的1到k,坏人的编号是k+1到2k。
我们希望求出m的最小值,使得最先出列的k个人都是坏人。
输入:
仅有的一个数字是k(0 输出: 使得最先出列的k个人都是坏人的m的最小值。 输入样例: 4 输出样例: 30 程序: #include longk,m,begin; intcheck(longremain){ longresult=(①)%remain; if(②){ begin=result;return1; } elsereturn0; } intmain(){ longi,find=0; scanf("%ld",&k); for(m=k;③;m++){ find=1;begin=0; for(i=0;i if(! check(④)){ find=0;break; } } printf("%ld\n",⑤); return0; } 2.逻辑游戏 题目描述: 一个同学给了我一个逻辑游戏。 他给了我图1,在这个图上,每一段边界都已经进行了编号。 我的任务是在图中画一条连续的曲线,使得这条曲线穿过每一个边界一次且仅穿过一次,而且曲线的起点和终点都在这整个区域的外面。 这条曲线是容许自交的。 对于图1,我的同学告诉我画出这样的一条曲线(图2)是不可能的,但是对于有的图形(比如图3),画出这样一条曲线是可行的。 对于给定的一个图,我想知道是否可以画出满足要求的曲线。 图1 图2 图3 图4 输入: 输入的图形用一个n×n的矩阵表示的。 矩阵的每一个单元里有一个0到255之间(包括0和255)的整数。 处于同一个区域的单元里的数相同,相邻区域的数不同(但是不相邻的区域里的数可能相同)。 输入的第一行是n(0 以下的n行每行包括n个整数,分别给出对应的单元里的整数(这n个整数之间用空格分开)。 图4给出了输入样例对应的图形。 输出: 当可以画出满足题意的曲线的时候,输出“YES”;否则,输出“NO”。 输入样例: 3 112 122 112 输出样例: YES 程序: #include #include intorig,n,ns,a[102][102],bun; intd[]={1,0,-1,0,0,1,①}; voidplimba(intx,inty){ inti,x1,y1; a[x][y]=-a[x][y]; if(abs(a[x-1][y])! =orig&&(②! =a[x-1][y] ||abs(a[x][y-1])! =orig))ns++; if(abs(a[x+1][y])! =orig&&(a[x+1][y-1]! =a[x+1][y] ||abs(a[x][y-1])! =orig))ns++; if(abs(a[x][y-1])! =orig&&(③! =a[x][y-1] ||abs(a[x-1][y])! =orig))ns++; if(abs(a[x][y+1])! =orig&&(a[x-1][y+1]! =a[x][y+1] ||abs(a[x-1][y])! =orig))ns++; for(i=0;i<4;i++){ x1=x+d[2*i];y1=y+④; if(x1>=1&&x1<=n&&y1>=1&&y1<=n&&⑤) plimba(x1,y1); } } intmain(){ inti,j; bun=1; scanf("%d",&n); for(i=0;i<=n+1;i++) for(j=0;j<=n+1;j++)a[i][j]=0; a[0][0]=-1;a[n+1][0]=-1; a[0][n+1]=-1;a[n+1][n+1]=-1; for(i=1;i<=n;i++) for(j=1;j<=n;j++)scanf("%d",&(a[i][j])); for(i=1;i<=n;i++) for(j=1;j<=n;j++){ if(a[i][j]>-1){ ns=0;⑥; plimba(i,j); if(ns%2==1)bun=0; } } if(bun)printf("YES\n");elseprintf("NO\n"); return0; } '2005 1.木材加工 题目描述: 木材厂有一些原木,现在想把这些木头切割成一些长度相同的小段木头(木头有可能有 剩余),需要得到的小段的数目是给定了的。 当然,我们希望得到的小段越长越好,你的任 务是计算能够得到的小段木头的最大长度。 木头长度的单位是cm。 原木的长度都是正整数,我们要求切割得到的小段木头的长度 也是正整数。 输入: 第一行是两个正整数N和K(1≤N≤10000,1≤K≤10000),N是原木的数目, K是需要得到的小段的数目。 接下来的N行,每行有一个1到10000之间的正整数,表示一根原木的长度。 输出: 输出能够切割得到的小段的最大长度。 如果连1cm长的小段都切不出来,输出”0”。 输入样例: 37 232 124 456 输出样例: 114 程序: #include intn,k,len[10000]; intisok(intt){ intnum=0,i; for(i=0;i if(num>=k)break; num=①; } if(②)return1; elsereturn0; } intmain(){ inti,left,right,mid; scanf("%d%d",&n,&k); right=0; for(i=0;i scanf("%d",&(len[i])); if(right } right++; ③; while(④ mid=(left+right)/2; if(⑤)right=mid; elseleft=mid; } printf("%d\n",left); return0; } 2.N叉树 题目描述: 我们都了解二叉树的先根遍历,中根遍历和后根遍历。 当知道先根遍历的结果和中根遍 历结果的时候,我们可以唯一的确定二叉树;同样的,如果知道了后根遍历的结果和中根遍 历结果,二叉树也是唯一确定的。 但是如果只知道先根遍历和后根遍历的结果,二叉树就不 是唯一的了。 但是我们可以计算满足条件的不同二叉树的一共有多少个。 这不是一个很困难 的问题,稍微复杂一点,我们把这个问题推广到N叉树。 我们用小写英文字母来表示N叉树的结点,不同的结点用不同的字母表示。 比如,对 于4叉树,如果先根遍历的结果是abdefgc,后根遍历的结果是defgbca,那么我们可以 得到6个不同的4叉树(如下图)。 输入: 输入数据包括3行。 第一行是一个正整数N(1≤N≤20),表示我们要考虑N叉树。 第二行和第三行分别是两个字符串序列,分别表示先根遍历和后根遍历的结果。 输出: 输出不同的N叉树的数目。 题目中给的数据保证得到的结果小于2 31 。 输入样例: 4 abdefgc defgbca 输出样例: 6 程序: #include #include charstr1[100],str2[100]; intN; longcom[100][100]; longgetcom(intx,inty){ if(y==0||x==y)①; elseif(com[x][y]! =0)returncom[x][y]; else{ com[x][y]=getcom(x-1,y)+②; returncom[x][y]; } } longcount(inta,intb,intc){ longsum=1; intk=0; ints=a+1,t=c,p; if(a==b)return1; while(s<=b){ p=t; while(str1[s]! =str2[t])t++; sum=sum*count(s,s+t-p,p); s=③; ④; k++; } return⑤*getcom(N,k); } intmain(){ intlen; scanf("%d",&N); scanf("%s%s",str1,str2); len=strlen(str1); printf("%ld\n",count(⑥)); return0; } '2006 1.(选排列)下面程序的功能是利用递归方法生成从1到n(n<10)的n个数中取k(1<=k<=n)个数的 全部可能的排列(不一定按升序输出)。 例如,当n=3,k=2时,应该输出(每行输出5个排列): 1213212332 31 程序: #include intn,k,a[10]; longcount=0; voidperm2(intj) {inti,p,t; if(①) {for(i=k;i<=n;i++) {count++; t=a[k];a[k]=a[i];a[i]=t; for(②) printf("%1d",a[p]);/*"%1d"中是数字1,不是字母l*/ printf(""); t=a[k];a[k]=a[i];a[i]=t; if(count%5==0)printf("\n"); } return; } for(i=j;i<=n;i++) {t=a[j];a[j]=a[i];a[i]=t; ③; t=a[j];④; } } main() {inti; printf("\nEntryn,k(k<=n): \n"); scanf("%d%d",&n,&k); for(i=1;i<=n;i++)a[i]=i; ⑤; } 2.(TSP问题的交叉算子)TSP问题(TravelingSalesmanProblem)描述如下: 给定n个城市,构成一个完全图,任何两城市之间都有一个代价(例如路程、旅费等),现要构造遍历所有城市的环路,每个城市恰好经过一次,求使总代价达到最小的一条环路。 遗传算法是求解该问题的一个很有效的近似算法。 在该算法中,一个个体为一条环路,其编码方法之一是1到n这n个数字的一个排列,每个数字为一个城市的编号。 例如当n=5时,“34215”表示该方案实施的路线为3->4->2->1->5->3。 遗传算法的核心是通过两个个体的交叉操作,产生两个新的个体。 下面的程序给出了最简单的一种交叉算法。 具体过程如下: (1)选定中间一段作为互换段,该段的起止下标为t1,t2,随机生成t1,t2后,互换两段。 (2)互换后,在每个新的排列中可能有重复数字,因而不能作为新个体的编码,一般再做两步处理: (2.1)将两个互换段中,共同的数字标记为0,表示已处理完。 (2.2)将两个互换段中其余数字标记为1,按顺序将互换段外重复的数字进行替换。 例如: n=12,两个个体分别是: a1: 1354*2679*1012811 a2: 32112*671011*8549 t1=5,t2=8。 上述每一行中,两个星号间的部分为互换段。 假定数组的下标从1开始,互换后有: a1: 1354*671011*1012811 a2: 32112*2679*8549 然后,将数字6,7对应的项标记为0,星号内数字2,9,10,11对应的项标记为1,并且按顺序对应关系为: 10<->2,11<->9。 于是,将a1[9]=10替换为a1[9]=2,将a2[2]=2替换为a2[2]=10,类似再做第2组替换。 这样处理后,就得到了两个新个体: a1: 135467101121289 a2: 310112267985411 (3)输出两个新个体的编码。 (4) 程序: #include #include #defineN20 inta1[N],a2[N],kz1[N],kz2[N],n; intrand1(intk) {intt=0; while(t<2||t>k) t=(int)((double)rand()/RAND_MAX*k); returnt; } voidread1(inta[],intm) {读入数组元素a[1]至a[m],a[0]=0,略。 } voidwrt1(inta[],intm) {输出数组元素a[1]至a[m],略。 } voidcross(inta1[],inta2[],intt1,intt2,intn) {inti,j,k,t,kj; for(i=t1;i<=t2;i++) {t=a1[i];①; } for(i=1;i<=n;i++) if(i kz1[i]=kz2[i]=-1; else ②; for(i=t1;i<=t2;i++) for(j=t1;j<=t2;j++) if(a1[i]==a2[j]) {③;break; } for(i=t1;i<=t2;i++) if(kz1[i]==1) {for(j=t1;j<=t2;j++) if(kz2[j]==1) {kj=j;break; } for(j=1;j<=n;j++) if(④) {a1[j]=a2[kj];break; } for(j=1;j<=n;j++) if(⑤) {a2[j]=a1[i];break; } kz1[i]=kz2[kj]=0; } } main() {intk,t1,t2; printf("input(n>5): \n");scanf("%d",&n); printf("inputarray1(%d'numbers): \n",n);read1(a1,n); printf("inputarray2(%d'numbers): \n",n);read1(a2,n); t1=rand1(n-1); do {t2=rand1(n-1); }while(t1==t2); if(t1>t2) {k=t1;t1=t2;t2=k; } ⑥ wrt1(a1,n);wrt1(a2,n); } '2007 1.(格雷码,GrayCode) 格雷码是对十进制数的一种二进制编码。 编码顺序与相应的十进制数的大小不一致。 其特点是: 对于两个相邻的十进制数,对应的两个格雷码只有一个二进制位不同。 另外,最大数与最小数之间也仅有一个二进制位不同,以4位二进制数为例,编码如下: 十进制数格雷码十进制数格雷码 0000081100 1000191101 20011101111 30010111110 40110121010 50111131011 60101141001 70100151000 如果把每个二进制的位看作一个开关,则将一个数变为相邻的另一个数,只须改动一个开关。 因此,格雷码广泛用于信号处理、数-模转换等领域。 下面程序的任务是: 由键盘输入二进制数的位数n(n<16),再输入一个十进制数m(0≤m<2n),然后输出对应于m的格雷码(共n位,用数组gr[]存放)。 为了将程序补充完整,你必须认真分析上表的规律,特别是对格雷码固定的某一位,从哪个十进制数起,由0变为1,或由1变为0。 #include main() {intbound=1,m,n,i,j,b,p,gr[15]; printf("inputn,m\n"); scanf("%d%d",&n,&m); for(i=1;i<=n;i++)bound=①; if(m<0||m>=bound) {printf("Dataerror! \n"); ②; } b=1; for(i=1;i<=n;i++) {p=0;b=b*2; for(③;j<=m;j++) if(④) p=1-p; gr[i]=p; } for(i=n;⑤) printf("%1d",gr[i]);/*在"%1d"中出现的是数字1,不是字母l*/ printf("\n"); } 2.(连续邮资问题)某国发行了n种不同面值的邮票,并规定每封信最多允许贴m张邮票,在这些约束下,为了能贴出{1,2,3,…,maxvalue}连续整数集合的所有邮资,并使maxvalue的值最大,应该如何设计各邮票的面值? 例如,当n=5、m=4时,面值设计为{1,3,11,15,32},可使maxvalue达到最大值70(或者说,用这些面值的1至4张邮票可以表示不超过70的所有邮资,但无法表示邮资71。 而用其他面值的1至4张邮票如果可以表示不超过k的所有邮资,必有k≤70)。 下面是用递归回溯求解连续邮资问题的程序。 数组x[1: n]表示n种不同的邮票面值,并约定各元素按下标是严格递增的。 数组bestx[1: n]存放使maxvalue达到最大值的邮票面值(最优解),数组y[maxl]用于记录当前已选定的邮票面值x[1: i]能贴出的各种邮资所需的最少邮票张数。 请将程序补充完整。 #include #defineNN20 #definemaxint30000 #definemaxl500/*邮资的最大值*/ intn,m,bestx[NN],x[NN],y[maxl],maxvalue=0; voidresult() {输出结果: 最大值: maxvalue及最优解: bestx[1: n](略) } voidbacktrace(inti,intr) {intj,k,z[maxl]; for(j=0;j<=①;j++) if(y[j] for(k=1;k<=m-y[j];k++) if(y[j]+k<=y[②]) y[③]=y[j]+k; while(y[r] if(i>n) {if(r-1>maxvalue) {maxvalue=④; for(j=1;j<=n;j++) bestx[j]=x[j]; } return; } for(k=0;k z[k]=y[k]; for(j=⑤;j<=r;j++) {x[i]=j; ⑥; for(k=0;k y[k]=z[k]; } } voidmain() {intj; printf("inputn,m: \n"); scanf(“%d%d”,&n,&m); for(j=1;j y[j]=maxint; y[0]=0;x[0]=0;x[1]=1; backtrace(2,1); result(); } '2008 1.(找第k大的数)给定一个长度为1,000,000的无序正整数序列,以及另一个数n(1<=n<=1000000),接下来以类似快速排序的方法找到序列中第n大的数(关于第n大的数: 例如序列{1,2,3,4,5,6}中第3大的数是4)。 #include #include inta[1000001],n,ans=-1; voidswap(int*a,int*b) { intc; c=*a;*a=*b;*b=c; } intFindKth(intleft,intright,intn) { inttmp,value,i,j; if(left==right)returnleft; tmp=rand()%(right-left)+left; swap(&a[tmp],&a[left]); value=① i=left; j=right; while(i { while(i
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- NOIP 初赛 试题 分类 汇总 完善 程序 部分