您的位置:首页 > 编程语言

【用膝盖写代码系列】(2):NOIP2011普及组复赛题目详解

2016-08-22 23:01 501 查看
这里是NOIP2011的急救现场,我已经准备好了救护车。

那么我们从第一题开始

第一题:数字反转

题意简述:给一个不超过10位的数(包括负号),输出这个数的反转。

(如:123,输出321)

陷阱提示:记得要预处理负号以及前导0

我对它的类型评估: 模拟

思路描述:这道题十分简单,用字符串读入,预处理负号,然后从后往前for,处理前导0,就可AC。

我的代码:

#include <cstdio>
#include <cstring>

int main(){
int i,j,n,m;
char a[13];
scanf("%s",&a);
int len = strlen(a);
if(len == 1 && a[len-1] == 0) {
printf("0");
return 0;
}
if(a[0] == '-') printf("-");
len--;
// printf("a[len] = %c\n",a[len]);
while(a[len] == '0') len--;
for(i=len;i>=0;i--) if(a[i] >= '0' && a[i] <= '9') printf("%c",a[i]);
}


洛谷原题:http://www.luogu.org/problem/show?pid=1307

第一题完。

第二题:统计单词数

(本人的内心:啊!!!!这是谁出的题目!拖出去大阿十遍!)

题目简述:给定一个单词以及一篇文章,求这个单词在文章的出现次数以及第一次出现的位置

陷阱提示:要注意全字匹配,不能单单截取单词的一部分!

我对它的类型评估:字符串操作、模拟

思路描述:这道题好坑啊TAT,主要原因是它的文章是有纯空格单词的…..这道题的思想就是:先把字母大小写统一(本题不区分大小写,所以要统一),然后枚举每一个文章中的单词,与原本的单词进行比较。

枚举的过程:枚举每一个文章中的字母,然后从当前字母A开始向后枚举所需求单词B的长度,然后A与B比较,如果完全相同,然后再判断单词A的前、后是否都是空格(否则有可能出现枚举一个单词一部分的情况),如果是第一次更新,就把答案设置为枚举时所循环的值(一般是i),此做法能AC、

我的代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
using namespace std;

int main(){
int i,j,n,m;
char word[12];
char text[1000001];
//freopen("a.in","r",stdin);
gets(word);
gets(text);
int lenw = strlen(word),lent = strlen(text);
int count = 0,cur = -1;
int sum=0;
bool flag = true;
for(i=0;i<lenw;i++) {
if(word[i] >= 'a' && word[i] <= 'z')
word[i] = word[i]&~(1<<5);//小写转大写
}
for(i=0;i<lent;i++) {
if(text[i] >= 'a' && text[i] <= 'z')
text[i] = text[i]&~(1<<5);//同上
}
for(i=0;i<=lent;i++){
for(j=0;j<lenw;j++){
if(text[i+j]!= word[j]) flag = false;
}
if(flag) {
if((text[i-1] == ' ' && text[i+lenw] == ' ')|| i==0){
count++;
if(cur==-1) {
cur = i;
}
}
}
flag = true;
}
if(cur==-1) puts("-1");
else printf("%d %d",count,cur);
return 0;
}


洛谷原题:http://www.luogu.org/problem/show?pid=1308

第二题总结:每一道题都有一个陷阱,切记在考场上要考虑到可能出现的数据!

第二题完。

第三题:瑞士轮

题意简述:给定n个人的实力以及分数,每一次比赛都让相差一名的人进行比赛(第一与第二,第三与第四,以此类推),赢的人得1分,然后重新排名。先给出人数n*2,场数r,以及q,求r场后排名q的选手编号是多少

陷阱提示:此题用sort会TLE

数据范围:对于100%的数据,

1 ≤ N ≤ 100,000,1 ≤ R ≤ 50,1 ≤ Q ≤ 2N,0 ≤ s1, s2, …, s2n≤108,1 ≤w1, w2 , …, w2n≤ 108

我对它的类型评估:模拟

*本题涉及函数:*merge

merge(数组A,A的结尾,数组B,B的结尾,输出数组C,交换函数)


这个函数的作用是合并数组A与数组B,若A、B是有序的,即完成一次归并排序。

思路描述:这道题其实就如题面描述一般进行模拟即可,记住每一次比赛模拟结束之后,记录一下成功者与失败者,最后用merge来合并(因为这两个数组一定有序)

我的代码

#include <cstdio>
#include <algorithm>
#include <queue>

struct Player{
int score,num,val;
}player[200002];

bool cmp(Player a,Player b){
if(a.score == b.score) return a.num < b.num;
else return a.score > b.score;
}
Player winner[200001],loser[200001];
int main(){
int i,j,n,m,q,r,winner_count=0,loser_count=0;
scanf("%d%d%d",&n,&r,&q);
for(i=1;i<=n*2;i++){
scanf("%d",&player[i].score);
player[i].num = i;
}
for(i=1;i<=n*2;i++)
scanf("%d",&player[i].val);
std::sort(player+1,player+1+(2*n),cmp);
for(i=1;i<=r;i++){
winner_count = loser_count = 0;
for(j=1;j<=n*2;j+=2){
if(player[j].val > player[j+1].val) {
player[j].score++;
winner[winner_count++] = player[j];
loser[loser_count++] = player[j+1];
}
else{
player[j+1].score++;
winner[winner_count++] = player[j+1];
loser[loser_count++] = player[j];
}
}
std::merge(winner,winner+winner_count,loser,loser+loser_count,player+1,cmp);
}

printf("%d",player[q].num);
}


洛谷原题:http://www.luogu.org/problem/show?pid=1309

第三题完。

第四题:表达式的值

题意简述:

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



运算的优先级是:

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

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

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

陷阱提示:无

我对它的类型评估:栈、动态规划

思路描述:

这道题是我做过最难的NOIP题目。

这道题的步骤很简单:

①:将式子从+(x)的形式变成的带有空的形式

②:将式子从中缀表达式化作后缀表达式

③:进行动态规划

状态转移方程:

设f[i][0]表示第i个空,能填0,f[i][1]表示第i个空,能填1

那么转移方程就出来了:

switch(w_c[i]){
case '+':{
f[top-1][1] = (f[top][0] * f[top-1][1] + f[top][1] * f[top-1][0] + f[top][1] * f[top-1][1])%10007;
f[top-1][0] = (f[top][0] * f[top-1][0]) % 10007;
break;
}
case '*':{
f[top-1][0] = (f[top][0] * f[top-1][1] + f[top][1] * f[top-1][0] + f[top][0] * f[top-1][0])%10007;
f[top-1][1] = (f[top][1] * f[top-1][1]) % 10007;
break;
}
}


我的代码:

#include <cstdio>
#include <stack>
#include <iostream>
using namespace std;
int i,j,n,m;
string s,w_c;
char str[100001],first[3] = {'(','+','*'};
int f[100001][2];
stack<char> op;
int OPS(char a){
int i;
for(i=0;i<3;i++){
if(a == first[i]) return i;
}
return -1;
}
inline void ChangeS(){
int i;
for(i=0;i<n;i++){
if(str[i] == '(') s.push_back(str[i]);
else if(str[i] == ')' && str[i-1] != ')') s+="_)";
else if(str[i]!=')') {
s.push_back('_');
s.push_back(str[i]);
}
else s.push_back(str[i]);
if(s.at(s.size()-1)!=')') s.push_back(str[i]);
}
}

inline void ChangeE(){
size_t i;
char t;
for(i=0;i<s.size();i++){
t = s.at(i);
if(t == '_') w_c.push_back(t);
else {if(t != ')'){
if(t == '(') op.push(t);
else if(op.empty()) op.push(t);
else {
while(!op.empty() && op.top() >= OPS(t)){
w_c.push_back(op.top());
op.pop();
}
op.push(t);
}
}
else
while(!op.empty() && op.top()!='('){
w_c.push_back(op.top());
op.pop();
}
}
}
}
void work(){
size_t i,top(0);
size_t l = w_c.size();
for(i=0;i<l;i++) {
if(w_c[i] == '_'){
f[top][0] = f[top][1] = 1;
top++;
}
else{
--top;
switch(w_c[i]){ case '+':{ f[top-1][1] = (f[top][0] * f[top-1][1] + f[top][1] * f[top-1][0] + f[top][1] * f[top-1][1])%10007; f[top-1][0] = (f[top][0] * f[top-1][0]) % 10007; break; } case '*':{ f[top-1][0] = (f[top][0] * f[top-1][1] + f[top][1] * f[top-1][0] + f[top][0] * f[top-1][0])%10007; f[top-1][1] = (f[top][1] * f[top-1][1]) % 10007; break; } }
}
}
printf("%d",f[top-1][0]);
}

int main(){
scanf("%d",&n);
scanf("%s",&str);
ChangeS();
ChangeE();
work();
return 0;
}


洛谷原题:http://www.luogu.org/problem/show?pid=1310

第四题完。

各位再见,我开救护车去飙车了88
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: