第一关——2005NOIP提高组

 

20:05:51   青春是挽不回的水,转眼消失在指尖。用力的浪费,再用力的后悔。——五月天《疯狂的世界》

第一题 谁拿了最多的奖学金

描述

某校的惯例是在每学期的期末考试之后发放奖学金。发放的奖学金共有五种,获取的条件各自不同:

1) 院士奖学金,每人8000元,期末平均成绩高于80分(>80),并且在本学期内发表1篇或1篇以上论文的学生均可获得;

2) 五四奖学金,每人4000元,期末平均成绩高于85分(>85),并且班级评议成绩高于80分(>80)的学生均可获得;

3) 成绩优秀奖,每人2000元,期末平均成绩高于90分(>90)的学生均可获得;

4) 西部奖学金,每人1000元,期末平均成绩高于85分(>85)的西部省份学生均可获得;

5) 班级贡献奖,每人850元,班级评议成绩高于80分(>80)的学生干部均可获得;

只要符合条件就可以得奖,每项奖学金的获奖人数没有限制,每名学生也可以同时获得多项奖学金。例如姚林的期末平均成绩是87分,班级评议成绩82分,同时他还是一位学生 干部,那么他可以同时获得五四奖学金和班级贡献奖,奖金总数是4850元。现在给出若干学生的相关数据,请计算哪些同学获得的奖金总数最高(假设总有同学能满足获得奖学 金的条件)。

输入

输入文件scholar.in的第一行是一个整数N(1 <= N <= 100),表示学生的总数。接下来的N行每行是一位学生的数据,从左向右依次是姓名,期末平均成绩,班级评议成绩,是 否是学生干部,是否是西部省份学生,以及发表的论文数。姓名是由大小写英文字母组成的长度不超过20的字符串(不含空格);期末平均成绩和班级评议成绩都是0到100之间 的整数(包括0和100);是否是学生干部和是否是西部省份学生分别用一个字符表示,Y表示是,N表示不是;发表的论文数是0到10的整数(包括0和10)。每两个相邻数据项之 间用一个空格分隔。

输出

输出文件scholar.out包括三行,第一行是获得最多奖金的学生的姓名,第二行是这名学生获得的奖金总数。如果有两位或两位以上的学生获得的奖金最多,输出他们之中在输入 文件中出现最早的学生的姓名。第三行是这N个学生获得的奖学金的总数。

样例输入

4

YaoLin 87 82 Y N 0

ChenRuiyi 88 78 N Y 1

LiXin 92 88 N N 0

ZhangQin 83 87 Y N 1

样例输出

ChenRuiyi

9000

28700

这道题重在打代码,适合用结构体,变量较多

#include<bits/stdc++.h>
using namespace std;
struct as{
    char st[20];
    int c;
    int j;
    char a;
    char b;
    int d;
    int jxj=0;
}ss[1003];
int main()
{
    int n,p,q,sum=0;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>ss[i].st;
        cin>>ss[i].c;
        cin>>ss[i].j;
        cin>>ss[i].a;
        cin>>ss[i].b;
        cin>>ss[i].d;
        if(ss[i].d>=1&&ss[i].c>80)ss[i].jxj+=8000;
        if(ss[i].c>85&&ss[i].j>80)ss[i].jxj+=4000;
        if(ss[i].c>90)ss[i].jxj+=2000;
        if(ss[i].c>85&&ss[i].b=='Y') ss[i].jxj+=1000;
        if(ss[i].j>80&&ss[i].a=='Y') ss[i].jxj+=850;
    }
    p=ss[1].jxj;
    for(int i=1;i<=n;i++)
    {
        if(ss[i].jxj>p)
        {
            p=ss[i].jxj;
            q=i;
        }
        sum+=ss[i].jxj;
    }
    cout<<ss[q].st<<endl<<p<<endl<<sum; 
    return 0; 
}

 像这种简单的题一定要仔细仔细再仔细!!!

21:41:23   如果 另个时空 另个身体 能不能 换另一种 结局  想见你 只想见你 未来过去 我只想见你  ——《想见你想见你想见你》

第二题 过河

描述

在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。

题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。

输入

输入文件river.in的第一行有一个正整数L(1 <= L <= 109),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个 数,其中1 <= S <= T <= 10,1 <= M <= 100。第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一 个空格隔开。

输出

输出文件river.out只包括一个整数,表示青蛙过河最少需要踩到的石子数。

样例输入

10

2 3 5

2 3 5 6 7

样例输出

2

这道题是一道dp,但是如果只用简单的dp,就只能得到L<1000的那30分。首先,c++的空间复杂度是不支持数组开10^9这个大小的,其次,for循环后面的长度,时间复杂度也会超出范围,所以,这道题需要做一个优化。就是路径很长,但是石子数是很少的,而且青蛙最优走法是不走到石子的地方,所以只需要在路径的时候不断mod一次,就可以是程序大大优化。数组也没有必要开到10^9。

状态转移方程:f[i]=min(f[i],f[i-k]+vis[i]);

#include<bits/stdc++.h>
using namespace std;
int s,t,l,m,a[1000009],f[100009];
bool vis[100001];
int main()
{
    scanf("%d%d%d%d",&l,&s,&t,&m);
    for(int i=1;i<=m;i++)
    cin>>a[i];
    memset(f,60,sizeof(f));
    f[0]=0;
    int o=0;
    sort(a,a+m+2);
    for(int i=1;i<=m+1;i++) 
    {
        if(a[i]-a[i-1]<=t*s)
        o+=a[i]-a[i-1];
        else
        o+=(a[i]-a[i-1])%t+t;
        vis[o]=true;
    }
    for(long long i=1;i<=o+t;i++)
    { 
        for(int k=s;k<=t;k++)
        {
            if(i-k>=0)
            f[i]=min(f[i],f[i-k]+vis[i]);
        }
    }
    int ans=999999;
    for(int i=o;i<=o+t;i++)
    ans=min(ans,f[i]);
    printf("%d",ans);
    return 0;
}

22:20:30  背影是真的人是假的  没什么执着  一百年前你不是你我不是我  ——《百年孤寂》

第三题 篝火晚会【O(n)】

总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB

描述

佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有n个同学, 编号从1到n。一开始,同学们按照1,2,……,n的顺序坐成一圈,而实际上每个人都有两个最希望相邻的同学。如何下命令调整同学的次序,形成新的一个圈,使之符合同学们的 意愿,成为摆在佳佳面前的一大难题。

佳佳可向同学们下达命令,每一个命令的形式如下:

(b1, b2,... bm -1, bm)

这里m的值是由佳佳决定的,每次命令m的值都可以不同。这个命令的作用是移动编号是b1,b2,…… bm –1,bm的这m个同学的位置。要求b1换到b2的位置上,b2换到b3的位置 上,……,要求bm换到b1的位置上。

执行每个命令都需要一些代价。我们假定如果一个命令要移动m个人的位置,那么这个命令的代价就是m。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?

输入

输入文件fire.in的第一行是一个整数n(3 <= n <= 50000),表示一共有n个同学。其后n行每行包括两个不同的正整数,以一个空格隔开,分别表示编号是1的同学最希望相邻 的两个同学的编号,编号是2的同学最希望相邻的两个同学的编号,……,编号是n的同学最希望相邻的两个同学的编号。

输出

输出文件fire.out包括一行,这一行只包含一个整数,为最小的总代价。如果无论怎么调整都不能符合每个同学的愿望,则输出-1。

样例输入

4

3 4

4 3

1 2

1 2

样例输出

2

这里并没有指这m个点必须是在一块的,只是说从原序列里找出了m个点进行交换,交换代价为m。这一点很坑,会想很久。

贪心:假设能够构成环,那么置换的代价一定可以等于所有不在位置上的数的个数。它原先一定不在自己的位置,因为一个位置的点只能有1个,而它已被某个点取到了。将初始序列的状态固定,枚举置换得到的序列,每次找一遍即可。

发现许多数都不会在位置上,这些状态都浪费了。于是对于置换得到的数列,找每个数距离初始状态的长,统计相同长的个数即可进行优化。

#include<bits/stdc++.h>
using namespace std;
const int N=50001;
int a[N],b[N];
int s[N],x[N],y[N],ans;
int i,j,n;
int main()
{
    scanf("%d",&n);
    for(i=1; i<=n; i++)
    scanf("%d%d",&a[i],&b[i]);
    s[1]=1,s[n]=a[1];
    s[2]=b[1];
    for(i=3;i<n;i++)
    if(a[s[i-1]]==s[i-2]) s[i]=b[s[i-1]];
    else s[i]=a[s[i-1]];
    for(i=1;i<=n;i++)
    {
        int t1=i+1,t2=i-1;
        if(t1>n) t1=1;
        if(t2<1) t2=n;
        if((s[t1]!=a[s[i]]||s[t2]!=b[s[i]])&&(s[t2]!=a[s[i]]||s[t1]!=b[s[i]]))
        {
            printf("-1");
            return 0;
        }
    }
    for(i=1;i<=n;i++)
    {
        x[(s[i]-i+n)%n]++;
        y[(s[i]+i-1)%n]++;
    }
    for(i=0;i<n;i++)
    {
        if (ans<x[i]) ans=x[i];
        if (ans<y[i]) ans=y[i];
    }
    printf("%d",n-ans);
    return 0;
}

23:07:51   世上无可代替是与你相拥的感受  人海里万中无一粒属于我的温柔  ——《一粒》

第四题  等价表达式

总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB

描述

明明进了中学之后,学到了代数表达式。有一天,他碰到一个很麻烦的选择题。这个题目的题干中首先给出了一个代数表达式,然后列出了若干选项,每个选项也是一个代数表 达式,题目的要求是判断选项中哪些代数表达式是和题干中的表达式等价的。

这个题目手算很麻烦,因为明明对计算机编程很感兴趣,所以他想是不是可以用计算机来解决这个问题。假设你是明明,能完成这个任务吗?

这个选择题中的每个表达式都满足下面的性质:

1. 表达式只可能包含一个变量‘a’。

2. 表达式中出现的数都是正整数,而且都小于10000。

3. 表达式中可以包括四种运算‘+’(加),‘-’(减),‘’(乘),‘^’(乘幂),以及小括号‘(’,‘)’。小括号的优先级最高,其次是‘^’,然后是‘’,最 后是‘+’和‘-’。‘+’和‘-’的优先级是相同的。相同优先级的运算从左到右进行。(注意:运算符‘+’,‘-’,‘*’,‘^’以及小括号‘(’,‘)’都是英文字符)

4. 幂指数只可能是1到10之间的正整数(包括1和10)。

5. 表达式内部,头部或者尾部都可能有一些多余的空格。

下面是一些合理的表达式的例子:

((a^1) ^ 2)^3,aa+a-a,((a+a)),9999+(a-a)a,1 + (a -1)^3,1^10^9……

输入

输入文件equal.in的第一行给出的是题干中的表达式。第二行是一个整数n(2 <= n <= 26),表示选项的个数。后面n行,每行包括一个选项中的表达式。这n个选项的标号分别 是A,B,C,D……

输入中的表达式的长度都不超过50个字符,而且保证选项中总有表达式和题干中的表达式是等价的。

输出

输出文件equal.out包括一行,这一行包括一系列选项的标号,表示哪些选项是和题干中的表达式等价的。选项的标号按照字母顺序排列,而且之间没有空格。

样例输入

( a + 1) ^2

3

(a-1)^2+4*a

a + 1+ a

a^2 + 2 1 + 1^2 + 10 -10 +a -a

样例输出

AC

提示

对于30%的数据,表达式中只可能出现两种运算符‘+’和‘-’;对于其它的数据,四种运算符‘+’,‘-’,‘*’,‘^’在表达式中都可能出现。对于全部的数据,表达式中 都可能出现小括号‘(’和‘)’。

要对一个表达式求值,首先要干掉表达式外面包着的多余的括号。若表达式内不含任何符号,只有数字(或者是一个a),那么我们直接返回数字(或者a^1)。否则,找到优先级最低的且不被括号包着的运算符。找最右边一个(因为我们的运算符都是左结合的)。

很容易想到暴力拆括号。但是,这个方法有2个麻烦的地方

1.需要实现一个储存因式的结构体并支持多种操作

2.(a-1)^10^10^10^10等表达式会让结构体储存的东西多到爆炸。

一般这种表达式等问题通常都会通过用栈来解决

#include<bits/stdc++.h>
#define ll long long
#define oo 10020123
#define mod 100000007
using namespace std;
ll xz(ll x,ll t){
    ll ans=1;
    for(int i=1;i<=t;i++)
    ans=ans*x%mod;
    return ans;
}
ll sby(char *s,int l,int r,ll a)
{
    int o=0,mm=52,mn=+oo,cnt=0,p[52],num=0;
    memset(p,0x3f,sizeof(p));
    for(int i=r;i>=l;i--)
    {
        if(s[i]==')')o+=100;
        if(s[i]=='(')o-=100;
        if(s[i]=='^')p[i]=o+3,cnt++;
        if(s[i]=='*')p[i]=o+2,cnt++;
        if(s[i]=='+')p[i]=o+1,cnt++;
        if(s[i]=='-')p[i]=o+1,cnt++;
        if(mn>p[i])mn=p[i],mm=i;
    }
    if(cnt==0)
    {
        for(int i=l;i<=r;i++)if(s[i]=='a')return a;
        for(int i=l;i<=r;i++)if(isdigit(s[i]))num=num*10+s[i]-'0';
        return num;
    }
    else
    {
        if(s[mm]=='^')return xz(sby(s,l,mm-1,a),sby(s,mm+1,r,a));
        if(s[mm]=='*')return (sby(s,l,mm-1,a)*sby(s,mm+1,r,a))%mod;
        if(s[mm]=='+')return (sby(s,l,mm-1,a)+sby(s,mm+1,r,a))%mod;
        if(s[mm]=='-')return (sby(s,l,mm-1,a)-sby(s,mm+1,r,a))%mod;
    }
    return 0;
}
int main()
{
    int l[27],n,ans[15];
    char ss[27][52];    
    scanf("%[^\r]",ss[0]),getchar();
    l[0]=strlen(ss[0]);
    cin>>n,getchar();
    for(int i=1;i<=n;i++)
    {
        scanf("%[^\r]",ss[i]),getchar();
        l[i]=strlen(ss[i]);
    }
    for(int i=0;i<=10;i++)
    ans[i]=sby(ss[0],0,l[0]-1,i-5);
    for(int i=1;i<=n;i++)
    {
        int f=1;
        for(int j=0;j<=10;j++)
        if(ans[j]!=sby(ss[i],0,l[i]-1,j-5))
        {
            f=0;
            break;
        }
        if(f)
        printf("%c",'A'+i-1);
    }
    return 0;
}

好啦!!!大功告成!!!

23:53:31  是你带我找到另一个天堂  远比想象中更美  我们怀抱里的这一个天堂  每一个梦想 有无限的快乐  ——《另一个天堂》

posted @ 2020-01-16 23:55  wybxz  阅读(257)  评论(1编辑  收藏  举报