NOIP普及组初赛完善题:看最近六年有无规律可循?

  • Post author:
  • Post category:其他




完善程序,每年两题,每题每空


2-4


分,共


28


分。



【解题步骤】



1


、仔细读题,尤其是题目给你的解题思路:解决什么问题?用的什么算法?输入输出是什么?


……



2


、要知道变量的含义,也可通过变量单词的意思知道,比如


sum


表示和,


que


表示队列等等。



3


、在充分了解前两点的基础上,先根据自己的想法大致想想:如果让你实现程序,你会怎么做。



4


、通读程序,理顺程序结构,千万不要因为程序很长而觉得气馁,有时程序越长,填空越简单。



5


、按照程序执行的顺序做,遇到难的先放一边,继续往下做。有些空格很简单,一下就能看出来的。



6


、到这步为止,程序大概意图就知道了,然后就是填比较难的几格了。这一点就靠你对程序的理解了。



7


、填完了以后,再执行一遍程序,有样例就结合样例,没样例就自己造数据模拟。



【解题技巧】



1


、变量初始化:这个得结合后面的运算确定,不过有些也很简单,如


sum=0


之类的。



2





for


循环初、终值:如果是嵌套的循环,可结合父循环或子循环确定。



3


、更新最优解:比较或赋值。



4


、要填的空格与某句对应,这样的例子在下面能找到很多。







NOIP2011-1.


子矩阵



给输入一个


n1*m1


的矩阵


a


,和


n2*m2


的矩阵


b


,问


a


中是否存在子矩阵和


b


相等。若存在,输出所有子矩阵左上角的坐标


:


若不存在输出


“There isno answer ”





#include<iostream>


using namespace std;


const int SIZE = 50;


int n1,m1,n2,m2,a[SIZE][SIZE],b[SIZE][SIZE];


int main()


{


int i,j,k1,k2;


bool good ,haveAns;


cin>>n1>>m1;


for(i=1;i<=n1;i++)


for(j=1;j<=m1;j++)


cin>>a[i][j];


cin>>n2>>m2;


for(i=1;i<=n2;i++)


for(j=1;j<=m2;j++)


cin>>b[i][j]


;


haveAns=false;


for(i=1;i<=n1-n2+1;i++)


for(j=1;j<=


m1-m2+1


;j++){




good=true


;


for(k1=1;k1<=n2;k1++)


for(k2=1;k2<=


m2


;k2++){


if(a[i+k1-1][j+k2-1]!=b[k1][k2])


good=false;


}


if(good){


cout<<i<<”<<j<<endl;




haveAns=true


;


}


}


if(!haveAns)


cout<<“Thereis no answer”<<endl;


return 0;


}







NOIP2011-2.


大整数开方



输入一个正整数


n ( 1 ≤n≤10^100


)


,试用二分法计算它的平方根的整数部分。


#include<iostream>


#include<string>


using namespace std;


const int SIZE=200;


struct hugeint{


int len,num[SIZE];


};



//


其中


len


表示大整数的位数


; num[1]


表示个位,


num[2]


表示十位,以此类推


hugeint times(hugeint a,hugeint b)



//


计算大整数


a





b


的乘积


{


int i,j;


hugeint ans;


memset(ans.num,0,sizeof(ans.num));


for(i=1;i<=a.len;i++)


for(j=1;j<=b.len;j++)


ans.num[i+j-1]


+=a.num[i]*b.num[j];


for(i=1;i<=a.len+b.len;i++){


ans.num[i+1]+=ans.num[i]/10;


ans.num[i]%=10


;



}


if(ans.num[a.len+b.len]>0)


ans.len=a.len+b.len;


else


ans.len=a.len+b.len-1;


return ans;


}


hugeint add(hugeint a,hugeint b)



//


计算大整数


a





b


的和


{


int i;hugeint ans;


memset(ans.num,0,sizeof(ans.num));


if(a.len>b.len)


ans.len=a.len;


else


ans.len=b.len;


for(i=1;i<=ans.len;i++){


ans.num[i]+=


a.num[i]+b.num[i]


;


ans.num[i+1]+=ans.num[i]/10;


ans.num[i]%=10;


}


if(ans.num[ans.len+1]>0)


ans.len++;


return ans;


}


hugeint average(hugeint a,hugeint b)



//


计算大整数


a





b


的平均数的整数部分


{


int i;


hugeint ans;


ans=add(a,b);


for(i=ans.len;i>=2;i–){


ans.num[i-1]+=(


ans.num[i] % 2


)*10;


ans.num[i]/=2;


}


ans.num[1]/=2;


if(ans.num[ans.len]==0)


ans.len–;


return ans;


}


hugeint plustwo(hugeint a)



//


计算大整数


a





2


之后的结果


{


int i;


hugeint ans;


ans=a;


ans.num[1]+=2;


i=1;


while((i<=ans.len)&&(ans.num[i]>=10) ){


ans.num[i+1]+=ans.num[i]/10;


ans.num[i]%=10;


i++;


}


if(ans.num[ans.len+1]>0)




ans.len++


;


return ans;


}


bool over(hugeint a,hugeint b)



//


若大整数


a>b


则返回


true


,否则返回


false




{


int i;


if(


a.len<b.len


)


return false;


if( a.len>b.len )


return true;


for(i=a.len;i>=1;i–){


if(a.num[i]<b.num[i])


return false;


if(a.num[i]>b.num[i])


return true;


}


return false;


}


int main()


{


string s;


int i;


hugeint target,left,middle,right;


cin>>s;


memset(target.num,0,sizeof(target.num));


target.len=s.length();


for(i=1;i<=target.len;i++)


target.num[i]=s[target.len-i]-


‘0’


;


memset(left.num,0,sizeof(left.num));


left.len=1;


left.num[1]=1;


right=target;


do{


middle=average(left,right);


if(over(


times(middle,middle),target


))


right=middle;


else


left=middle;


}while(!over(plustwo(left),right) );


for(i=left.len;i>=1;i–)


cout<<left.num[i];


return 0;


}








NOIP2012-1.


坐标统计



输入


n


个整点在平面上的坐标。对于每个点,可以控制所有位于它左下方的点


(





x





y


坐标都比它小


)


,它可以控制的点的数目称为





战斗力





。依次输出每个点的战斗力,最后输出战斗力最高的点的编号


(


如果两个点战斗力一样,输出较大的编号


)





#include<iostream>


using namespace std;


const int SIZE = 100;


int x[SIZE], y[SIZE], f[SIZE];


int n, i, j, max_f, ans;


int main()


{


cin>>n;


for (i = 1; i <= n; i++)


cin>>x[i]>>y[i];


max_f = 0;


for (i = 1; i <= n; i++)


{


f[i] =


0


;


for (j = 1; j <= n; j++)


{


if (x[j] <x[i] &&


y[j] < y[i]


;




f[i]++


;


}


if(


f[i] >= max_f


)


{


max_f = f[i];


ans = i


;


}


}


for (i = 1; i <= n; i++)


cout<<f[i]<<endl;


cout<<ans<<endl;


return o;


}







NOIP2012-2.


排列数



输入两个正整数


n, m (1 ≤n ≤20, 1 ≤m ≤n)


,在


1~n


中任取


m


个数,按字典序从小到大输出所有这样的排列。例如



输入


:


3 2



输出


:


1 2


1 3


2 1


2 3


3 1


3 2



#include<iostream>


#include<cstring>


using namespace std;


const int SIZE = 25;


bool used[SIZE];


int data[SIZE];


int n, m, i, j, k;


bool flag;


int main()


{


cin>>n>>m;


memset(used, false, sizeof(used));


for (i = 1; i <= m; i++)


{


data[i] = i;


used[i] =true;


}


flag = true;


while (flag)


{


for (i = 1; i<= m-1; i++) cout<<data[i]<<” “;


cout<<data[m]<<endl;


flag =


false


;


for (i = m; i>= 1; i–)


{




used[data[i]] = false


;


for (j =data[i]+1; j <= n; j++) if (!used[j])


{


used[j] =true;


data[i] =


j


;


flag = true;


break;


}


if (flag)


{


for (k = i+1;k <= m; k++)


for (j = 1; j<=


n


; j++) if (!used[j])


{


data[k] = j;


used[j] =true;


break;


}




break


;


}


}


}


}








NOIP2013-1.


序列重排


全局数组变量


a


定义如下


:


const int SIZE = 100;


int a[SIZE], n;



它记录着一个长度为


n


的序列


a[1], a[2], …, a[n]






现在需要一个函数,以整数


p (1 ≤ p≤ n)


为参数,实现如下功能


:


将序列


a


的前


p


个数与后


n – p


个数对调,且不改变这


p


个数


(





n – p


个数


)


之间的相对位置。例如,长度为


5


的序列


1, 2, 3, 4, 5


,当


p = 2


时重排结果为


3, 4, 5, 1, 2






有一种朴素的算法可以实现这一需求,其时间复杂度为


O(n)


、空间复杂度为


O(n):


void swap1(int p)


{


int i, j, b[SIZE];


for (i = 1; i <= p; i++)


b[


n–p+i


] = a[i];


for (i = p + 1; i <= n; i++)


b[i-p]=


a[i]


;


for(i=1;i<=


n


;i++)


a[i] = b[i];


}



我们也可以用时间换空间,使用时间复杂度为


O(n


2


)


、空间复杂度为


O(1)


的算法


:




void swap2(int p)


{


int i, j, temp;


for (i = p + 1; i <= n; i++) {


temp = a[i];


for(j=i;j>=


i–p+1


;j–)


a[j] = a[j – 1];


a[i – p]


= temp;


}


}








NOIP2013-2.


二叉查找树


二叉查找树具有如下性质:每个节点的值都大于其左子树上所有节点的值、小于其右子树上所有节点的值。试判断一棵树是否为二叉查找树。



输入的第一行包含一个整数


n


,表示这棵树有


n


个顶点,编号分别为


1, 2, …, n


,其中编号为


1


的为根结点。之后的第


i


行有三个数


value, left_child, right_child


,分别表示该节点关键字的值、左子节点的编号、右子节点的编号;如果不存在左子节点或右子节点,则用


0


代替。输出


1


表示这棵树是二叉查找树,输出


0


则表示不是。


#include <iostream>


using namespace std;


const int SIZE = 100;


const int INFINITE = 1000000;


struct node {


int left_child, right_child, value;


};


node a[SIZE];


int is_bst(int root, int lower_bound, int upper_bound)


{


int cur;


if (root == 0)


return 1;


cur = a[root].value;


if ((cur > lower_bound) && (


cur < upper_bound


) &&


(is_bst(a[root].left_child, lower_bound, cur)== 1) &&


(is_bst(


a[root].right_child


,


cur


,


upper_bound


)== 1))


return 1;


return 0;


}


int main()


{


int i, n;


cin>>n;


for (i = 1; i <= n; i++)


cin>>a[i].value>>a[i].left_child>>a[i].right_child;


cout<<is_bst(


1


,-INFINITE, INFINITE)<<endl;


return 0;


}








NOIP2014-1.


数字删除


下面程序的功能是将字符串中的数字字符删除后输出。请填空。


#include <iostream>


using namespace std;


int delnum(char *s) {


int i, j;


j = 0;


for (i = 0; s[i] != ‘\0’; i++)


if (s[i] < ‘0’


||


s[i] > ‘9’) {


s[j] = s[i];




j++


;


}


return


j


;


}


const int SIZE = 30;


int main() {


char s[SIZE];


int len, i;


cin.getline(s, sizeof(s));


len = delnum(s);


for (i = 0; i < len; i++)


cout<<


s[i]


;


cout << endl;


return 0;


}







NOIP2014-2.


最大子矩阵和



给出


m





n


列的整数矩阵,求最大的子矩阵和


(


子矩阵不能为空


)






输入第一行包含两个整数


m





n


,即矩阵的行数和列数。之后


m


行,每行


n


个整数,描述整个矩阵。程序最终输出最大的子矩阵和。


#include <iostream>


using namespace std;


const int SIZE = 100;


int matrix[SIZE + 1][SIZE + 1];


int rowsum[SIZE + 1][SIZE + 1];




//rowsum[i][j]


记录第


i


行前


j


个数的和


int m, n, i, j, first, last, area, ans;


int main() {


cin >> m >> n;


for (i = 1; i <= m; i++)


for (j = 1; j<= n; j++)


cin >>matrix[i][j];


ans = matrix


[1][1]


;


for (i = 1; i <= m; i++)


rowsum[i][0]=0


;


for (i = 1; i <= m; i++)


for (j = 1; j<= n; j++)


rowsum[i][j] =


rowsum[i][j-1]+matrix[i][j]


;


for (first = 1; first <= n; first++)


for (last = first;last <= n; last++) {


area=0


;


for (i = 1; i<= m; i++) {


area+=


rowsum[i][last]-rowsum[i][first-1]


;


if (area > ans)


ans = area;


if (area < 0)


area = 0;


}


}


cout << ans << endl;


return 0;


}








NOIP2015-1.


打印月历



输入月份


m(1≤m≤12)


,按一定格式打印


2015


年第


m


月的月历。例如,


2015





1


月的月历打印效果如下


(


第一列为周日


)






#include<iostream>


#include<string>


usingnamespace std;


const intdayNum[ ] = {-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};


int m,offset, i;


int main()


{


cin>> m;


cout<< “S\tM\tT\tW\tT\tF\tS” << endl;



//’\t’





TAB


制表符


offset=4


;


for(i= 1; i < m; i++)


offset =


(offset+dayNum[i])%7


;


for(i= 0; i < offset; i++)


cout << ‘\t’;


for(i=1;i<=


dayNum[m]


;i++)


{


cout<<


i


;


if(i == dayNum[m] ||


(offset+i)%7


==0)


cout << endl;


else


cout << ‘\t’;


}


return 0;


}







NOIP2015-2.


中位数


median



给定


n(n


为奇数且小于


1000)


个整数,整数的范围在


0~m(0<m<2^31)


之间,请使用二分法求这


n


个整数的中位数。所谓中位数,是指将这


n


个数排序之后,排在正中间的数。


#include<iostream>


usingnamespace std;


const intMAXN = 1000;


int n, i,lbound, rbound, mid, m, count;


int x[MAXN];


int main()


{


cin>> n >> m;


for(i= 0; i < n; i++)


cin >> x[i];


lbound= 0;


rbound= m;


while(


lbound<rbound


)


{


mid = (lbound + rbound) / 2;


count=0


;


for(i = 0; i < n; i++)


if(


x[i]>mid


)




count++


;


if(count > n / 2)


lbound = mid + 1;


else


rbound=mid


;


cout << mid << ” “<< lbound << ” ” << rbound << ” “<< count << endl;


}


cout<< rbound << endl;


return 0;


}








NOIP2016-1.


读入整数



请完善下面的程序,使得程序能够读入两个


int


范围内的整数,并将这两个整数分别输出,每行一个。输入的整数之间和前后只会出现空格或者回车。输入数据保证合法。





例如


:



输入


:


123  -789



输出


:


123


-789



#include<iostream>


using namespacestd;


int readint() {


int num = 0;



//


存储读取到的整数




int negative = 0;



//


负数标识




charc;



//


存储当前读取到的字符




c =cin.get();


while((c < ‘0’ || c > ‘9’) && c != ‘-‘)


c =


cin.get()


;


if (c== ‘-‘)


negative = 1;


else


num=c-‘0’


;


c =cin.get();


while(


c>=’0’&&c<=’9′


) {


num=num*10+c-‘0’


;


c = cin.get();


}


if(negative == 1)


return -num


;


return num;


}


int main() {


int a,b;


a =readint();


b =readint();


cout<< a << endl << b << endl;


return 0;


}








NOIP2016-2.


郊游活动






n


名同学参加学校组织的郊游活动,已知学校给这


n


名同学的郊游总经费为


A


元,与此同时第


i


位同学自己携带了


Mi


元。为了方便郊游,活动地点提供


B(≥n)


辆自行车供人租用,租用第


j


辆自行车的价格为


Cj


元,每位同学可以使用自己携带的钱或者学校的郊游经费,为了方便账务管理,每位同学只能为自己租用自行车,且不会借钱给他人,他们想知道最多有多少位同学能够租用到自行车。





本题采用二分法。对于区间


[l, r]


,我们取中间点


mid


并判断租用到自行车的人数能否达到


mid


。判断的过程是利用贪心算法实现的。




#include<iostream>


using namespacestd;


#define MAXN1000000


int n, B, A,M[MAXN], C[MAXN], l, r, ans, mid;


bool check(int nn){


intcount = 0, i, j;


i =


n-nn+1


;


j = 1;


while(i <= n) {


if (


M[i]<=C[j]


)


count += C[j] – M[i];


i++;


j++;


}


return


count<=A


;


}


void sort(int a[],int l, int r) {


int i= l, j = r, x = a[(l + r) / 2], y;


while(i <= j) {


while (a[i] < x) i++;


while (a[j] > x) j–;


if (i <= j) {


y = a[i]; a[i] = a[j]; a[j] = y;


i++; j–;


}


}


if (i< r) sort(a, i, r);


if (l< j) sort(a, l, j);


}


int main() {


int i;


cin>> n >> B >> A;


for (i= 1; i <= n; i++)


cin >> M[i];


for (i= 1; i <= B; i++)


cin >> C[i];


sort(M,1, n);


sort(C,1, B);


l = 0;


r = n;


while(l <= r) {


mid = (l + r) / 2;


if (


check(mid)


) {


ans = mid;


l = mid + 1;


} else


r =


mid-1


;


}


cout<< ans << endl;


return 0;


}