2011年NOIP普及组复赛真题解析

2011年NOIP普及组T1-数字反转

题目描述

给定一个整数,请将该数各个位上数字反转得到一个新数。新数也应满足整数的常见形式,即除非给定的原数为零,否则反转后得到的新数的最高位数字不应为零,例如输入-380,反转后得到的新数为-83。

输入格式

输入共 1 行,一个整数N。

-1,000,000,000 ≤ N≤ 1,000,000,000。

输出格式

输出共 1 行,一个整数,表示反转后的新数。

输入输出样例

输入样例1:
123
输出样例1:
321
输入样例2:
-380
输出样例2:
-83

耗时限制1000ms  内存限制64MB

解析

考点:循环结构,数位分离

参考代码

#include <bits/stdc++.h>
using namespace std;
int main(){
    int n,d=0;
    cin>>n;
    while(n){
        d=d*10+n%10;
        n/=10;
    }
    cout<<d;
    return 0;
}

2011年NOIP普及组T2-统计单词数

题目描述

一般的文本编辑器都有查找单词的功能,该功能可以快速定位特定单词在文章中的位置,有的还能统计出特定单词在文章中出现的次数。

现在,请你编程实现这一功能,具体要求是:给定一个单词,请你输出它在给定的文章中出现的次数和第一次出现的位置。注意:匹配单词时,不区分大小写,但要求完全匹配,即给定单词必须与文章中的某一独立单词在不区分大小写的情况下完全相同(参见样例1 ),

如果给定单词仅是文章中某一单词的一部分则不算匹配(参见样例2 )。

输入格式

第1 行为一个字符串,其中只含字母,表示给定单词;

第2 行为一个字符串,其中只可能包含字母和空格,表示给定的文章。

输出格式

只有一行,如果在文章中找到给定单词则输出两个整数,两个整数之间用一个空格隔开,分别是单词在文章中出现的次数和

第一次出现的位置(即在文章中第一次出现时,单词首字母在文章中的位置,位置从 0 开始);如果单词在文章中没有出现,

则直接输出一个整数-1。

输入输出样例

输入样例1:
To
to be or not to be is a question
输出样例1:
2 0
输入样例2:
to
Did the Ottoman Emprite lose its power at that time
输出样例2:
-1

说明

【数据范围】

1≤ 单词长度≤10。

1≤ 文章长度≤1,000,000

耗时限制1000ms   内存限制128MB

解析:

考点:字符串

#include<bits/stdc++.h>
using namespace std;

string s1, s2;
int ans, first, p;

// 字符串字符全部转大写 
string change(string s){
    for(int i = 0; i < s.size(); i++){
        if(s[i] >= 'a')
            s[i] -= 32;
    }
    return s;
}


int main(){
    getline(cin, s1);
    getline(cin, s2);
    s1 = change(s1);     // 转大写 
    s2 = change(s2);
    s1 = ' ' + s1 + ' '; // 左右加空格 
    s2 = ' ' + s2 + ' ';
    while(1){
        p = s2.find(s1, p);
        if(p != string::npos) {  // 能找到 
            if(ans == 0)   // 第一次找到 
                first = p;
            ans++; 
            p++;    // 往后找 
        }
        else
            break;
    }
    if(ans == 0)
        cout << -1 << endl;
    else
        cout << ans << " " << first << endl;
    return 0;
}

2011年NOIP普及组T3-瑞士轮

题目描述

背景:在双人对决的竞技性比赛,如乒乓球、羽毛球、国际象棋中,最常见的赛制是淘汰赛和循环赛。前者的特点是比赛场数少,每场都紧张刺激,但偶然性较高。后者的特点是较为公平,偶然性较低,但比赛过程往往十分冗长。本题中介绍的瑞士轮赛制,因最早使用于1895年在瑞士举办的国际象棋比赛而得名。它可以看作是淘汰赛与循环赛的折中,既保证了比赛的稳定性,又能使赛程不至于过长。

描述:2*N 名编号为 1~2N 的选手共进行R 轮比赛。每轮比赛开始前,以及所有比赛结束后,都会按照总分从高到低对选手进行一次排名。选手的总分为第一轮开始前的初始分数加上已参加过的所有比赛的得分和。总分相同的,约定编号较小的选手排名靠前。每轮比赛的对阵安排与该轮比赛开始前的排名有关:第1 名和第2 名、第 3 名和第 4名、……、第2K – 1 名和第 2K名、…… 、第2N – 1 名和第2N名,各进行一场比赛。每场比赛胜者得1 分,负者得 0 分。也就是说除了首轮以外,其它轮比赛的安排均不能事先确定,而是要取决于选手在之前比赛中的表现。

现给定每个选手的初始分数及其实力值,试计算在R 轮比赛过后,排名第 Q 的选手编号是多少。我们假设选手的实力值两两不同,且每场比赛中实力值较高的总能获胜。

输入格式

输入文件名为swiss.in 。

输入的第一行是三个正整数N、R 、Q,每两个数之间用一个空格隔开,表示有 2*N 名选手、R 轮比赛,以及我们关心的名次 Q。

第二行是2*N 个非负整数s1, s2, …, s2N,每两个数之间用一个空格隔开,其中 si 表示编号为i 的选手的初始分数。 

第三行是2*N个正整数w1 , w2 , …, w2N,每两个数之间用一个空格隔开,其中 wi 表示编号为i 的选手的实力值。

输出格式

输出文件名为swiss.out。

输出只有一行,包含一个整数,即R 轮比赛结束后,排名第 Q 的选手的编号。

输入输出样例

输入样例1:
2 4 2
7 6 6 7
10 5 20 15
输出样例1:
1

说明

【样例解释】

【数据范围】

对于30% 的数据,1 ≤ N ≤ 100;

对于50% 的数据,1 ≤ N ≤ 10,000 ;

对于100%的数据,1 ≤ N ≤ 100,000,1 ≤ R ≤ 50,1 ≤ Q ≤ 2N,0 ≤ s1, s2, …, s2N≤10^8,1 ≤w1, w2 , …, w2N≤ 10^8。

耗时限制1000ms   内存限制128MB

解析:

考点:模拟,归并排序,分治

参考代码

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int n,q,r;
struct stu{
	int id,s,w;           //编号,积分,实力值 
}p[2*N],win[N],lose[N];

bool cmp(stu a,stu b){
	if(a.s==b.s)return a.id<b.id;   //积分相同,按编号升序排序 
	return a.s>b.s;               //积分降序排序 
}
int main(){
	scanf("%d %d %d",&n,&r,&q);  //n对选手,r轮比赛,最终的第q名 
	for(int i=1;i<=2*n;i++){     
		scanf("%d",&p[i].s);     //读入选手的初始积分和编号 
		p[i].id=i;
	}
	for(int i=1;i<=2*n;i++)
		scanf("%d",&p[i].w);    //读入选手的是理智 
	sort(p+1,p+2*n+1,cmp);     //初始排名 
	for(int i=1;i<=r;i++){     //r轮比赛 
		int tot=0;
		for(int j=1;j<n*2;j+=2){
			if(p[j].w>p[j+1].w){   //j赢 
				p[j].s++;
				win[++tot]=p[j];   //j加入胜利组 
				lose[tot]=p[j+1];  //j+1加入败者组 
			}
			else{
				p[j+1].s++;
				win[++tot]=p[j+1];
				lose[tot]=p[j];
			}
		}
		//归并两个有序序列 
		int x=1,y=1,z=1;
	    while(x<=n&&y<=n){
	    	//比较,将较小者放入p数组 
			if(cmp(win[x],lose[y]))p[z++]=win[x++];
			else p[z++]=lose[y++];
		} 	
		//剩余队员存储p数组 
		while(x<=n)p[z++]=win[x++];
		while(y<=n)p[z++]=lose[y++];
	}
	printf("%d",p[q].id); //输出第q名的ID 
}

2011年NOIP普及组T4-表达式的值

题目描述

对于1 位二进制变量定义两种运算:

运算的优先级是:

1.先计算括号内的,再计算括号外的。

2.“× ”运算优先于“⊕”运算,即计算表达式时,先计算× 运算,再计算⊕运算。例如:计算表达式A⊕B × C时,先计算 B × C,其结果再与 A 做⊕运算。

现给定一个未完成的表达式,例如_+(_*_),请你在横线处填入数字0 或者1 ,请问有多少种填法可以使得表达式的值为0 。

输入格式

输入文件名为exp.in ,共 2 行。

第1 行为一个整数 L,表示给定的表达式中除去横线外的运算符和括号的个数。

第2 行为一个字符串包含 L 个字符,其中只包含’(’、’)’、’+’、’*’这4 种字符,其中’(’、’)’是左右括号,’+’、

’*’分别表示前面定义的运算符“⊕”和“×”。这行字符按顺序给出了给定表达式中除去变量外的运算符和括号。

输出格式

输出文件exp.out 共1 行。包含一个整数,即所有的方案数。注意:这个数可能会很大,请输出方案数对10007 取模后的结果。

输入输出样例

输入样例1:
4
+(*)
输出样例1:
3

说明

【输入输出样例说明】

 给定的表达式包括横线字符之后为:_+(_*_) 

 在横线位置填入(0 、0 、0) 、(0 、1 、0) 、(0 、0 、1) 时,表达式的值均为0 ,所以共有3种填法。 

【数据范围】

对于20% 的数据有 0 ≤ L ≤ 10。

对于50% 的数据有 0 ≤ L ≤ 1,000。

对于70% 的数据有 0 ≤ L ≤ 10,000 。

对于100%的数据有 0 ≤ L ≤ 100,000。

对于50% 的数据输入表达式中不含括号。

耗时限制1000ms  内存限制128MB

解析:

考点:栈+递推

思路

1.问题中的“+”相当于计算机中的“I”,“*”相当于计算机中的“&”;
2.运算优先级:“()”>“&”>“|”;

思路:枚举+中缀表达式计算:时间复杂度:0(N*2N)。
//共有2~n种中缀表达式,中缀表达式枚举时间复杂度是o(n)

计算表达式*的解:
        0*0=0,0*1=0,1*0=0,1*1=1,共3种可能是0,1种可能是1。

计算表达式*+的解:
        0*0+0=0,0*1+0=0,1*0+0=0,共3种可能是0,5种可能是1。

整个表达式为0的方案数=红色为0方案数*蓝色为0方案数=3*1=3。

将方案数作为表达式的操作数,+和*作为表达式的操作符,问题可转换为中缀表达式计算。

对于操作符(+):
①结果为0的方案数=左操作数为0的方案数*右操作数为0的方案数。
②结果为1的方案数=左操作数为0的方案数*右操作数为1的方案数+左操作数为1
的方案数*右操作数为0的方案数+左操作数为1的方案数*右操作数为1的方案数。

对于操作符(*):
①结果为0的方案数=左操作数为0的方案数*右操作数为1的方案数+左操作数为1的方案数*右操作数为0的方案数+左操作数为0的方案数*右操作数为0的方案数。

②结果为1的方案数=左操作数为1的方案数*右操作数为1的方案数。

初始表达式,所有的操作数0和1的方案数都是1。

参考代码:

#include<bits/stdc++.h>
using namespace std;
const int MOD=10007;
struct Node{
	int one,zero;   //记录1和0的数量 
};
stack<char> ops;    //操作符栈 
stack<Node> ds;        //操作数栈 
void calc(){
	Node a,b,c;
	b=ds.top();ds.pop();    //右操作数 
	a=ds.top();ds.pop();    //左操作数 
	char op=ops.top();ops.pop();   //运算符 
	if(op=='*'){
		c.one=(a.one*b.one)%MOD;       //(1*1) 
		c.zero=(a.zero*b.zero+a.zero*b.one+a.one*b.zero)%MOD; //(0*1,0*0,1*0)
		
	}
	else{
		c.one=(a.one*b.one+a.zero*b.one+a.one*b.zero)%MOD; //(1+0,0+1,1+1)
		c.zero=(a.zero*b.zero)%MOD;   //(0+0)
	}
	ds.push(c);      //运算结果入栈 
}
int level(char c){
	if(c=='+') return 1;
	if(c=='*') return 2;
	return 0;
}
int n;char c,pre;    //运算符方便初始化pre为右括号 
int main(){	
	cin>>n;
	Node d;d.zero=d.one=1; //运算数初始值 
	for(int i=1;i<=n;i++){
		cin>>c;
		//当前字符如果是左括号,或者当前字符不是左括号但其左边是右括号,这种情况不需要添加操作数 
		//除左括号的左边和右括号的右边,其他操作符的左边要添加操作数 
		if(c!='('&&pre!=')') ds.push(d);
		pre=c;
		if(c=='(') ops.push(c); //左括号直接入栈 
		else if(c==')'){   //右括号计算 直到遇到左括号 
			while(ops.top()!='(') calc();
			ops.pop();    //左括号出栈 
		}
		else{              //操作符 
			while(!ops.empty()&&level(ops.top())>=level(c)) calc();
			ops.push(c);   //操作符入栈 
		}
	}
	//最后一个是运算符,右边需要添加一个运算数 
	if(c=='+'||c=='*') ds.push(d);
	while(!ops.empty()) calc();
	cout<<ds.top().zero;
	return 0;
}

  • 20
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值