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

这里是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]!=')' && str[i-1] != ')') {
                s.push_back('_');
                s.push_back(str[i]);
            }
            else s.push_back(str[i]);  
        }
        if(s.at(s.size()-1)!=')') s.push_back('_');
    }

    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())&& OPS(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();
                }
                op.pop();
            }
        }  
    }    
    while(!op.empty()){
        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(); 
    //for(i=0;i<=w_c.length();i++) printf("%c",w_c[i]); 
    work();
    return 0;
}

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

第四题完。
各位再见,我开救护车去飙车了88
(很抱歉,最后一题的代码有一些问题,现在已修正。)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值