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

来源:互联网 发布:英剧四级一部 知乎 编辑:程序博客网 时间:2024/04/29 11:32

这里是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, …, s2n108,1 ≤w1, w2 , …, w2n108

我对它的类型评估:模拟

*本题涉及函数:*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 0