• NOIP2005题解


    传送门

    考查题型 dp 模拟 贪心 

    T1 

    谁拿了最多的奖学金

    题目描述

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

    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个学生获得的奖学金的总数。

    输入输出样例

    输入样例#1:
    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
    
    输出样例#1:
    ChenRuiyi
    9000
    28700
    

    说明

    2005提高组第一题

    题解

    模拟

    代码

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    
    int n,ans;
    struct P{
        string name;int id;
        int qm,bj;char gb,xb;
        int lw,jxj;
    }stu[102];
    
    bool cmp(P a,P b){
        if(a.jxj!=b.jxj)return a.jxj>b.jxj;
        return a.id<b.id;
    }
    
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
        cin>>stu[i].name>>stu[i].qm>>stu[i].bj>>stu[i].gb>>stu[i].xb>>stu[i].lw;
        stu[i].id=i;
    //    cout<<stu[i].name<<" "<<stu[i].qm<<" "<<stu[i].bj<<" "<<stu[i].gb<<" "<<stu[i].xb<<" "<<stu[i].lw<<endl;;
        if(stu[i].qm>80&&stu[i].lw>=1)stu[i].jxj+=8000;
        if(stu[i].qm>85&&stu[i].bj>80)stu[i].jxj+=4000;
        if(stu[i].qm>90)stu[i].jxj+=2000;
        if(stu[i].qm>85&&stu[i].xb=='Y')stu[i].jxj+=1000;
        if(stu[i].bj>80&&stu[i].gb=='Y')stu[i].jxj+=850;
        ans+=stu[i].jxj;
        }
        sort(stu+1,stu+n+1,cmp);
        cout<<stu[1].name<<endl;
        cout<<stu[1].jxj<<endl;
        cout<<ans<<endl;
        return 0;
    }

    T2 

    过河

    题目描述

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

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

    输入输出格式

    输入格式:

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

    输出格式:

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

    输入输出样例

    输入样例#1:
    10
    2 3 5
    2 3 5 6 7
    
    输出样例#1:
    2

    说明

    对于30%的数据,L le 10000L10000;

    对于全部的数据,L le 10^9L109​​。

    2005提高组第二题

     题解

    dp 注意压缩路径 至于为什么mod那个数我也不知道

    网上有人证距离大于81的时候任何一个点都能跳到,不过也可以模所有可能跳的

    长度的最小公倍数 这个是好理解的

    代码

    #include<iostream>
    #include<algorithm> 
    #include<cstdio>
    using namespace std;
    int ll,s,t,m,d[101],pos[101],f[1000005],stone[1000009];
    int ans=214748;
    int main(){
        scanf("%d",&ll);
        scanf("%d%d%d",&s,&t,&m);
        for(int i=1;i<=m;i++)scanf("%d",&pos[i]);
        sort(pos+1,pos+m+1); 
        if(s==t){ans=0;
            for(int i=1;i<=m;i++)if(pos[i]%s==0)ans++;
            cout<<ans<<endl;
            return 0;
        }
        for(int i=1;i<=m;i++)if(pos[i]-pos[i-1]>100){
            d[i]=(pos[i]-pos[i-1])%100;
        }else d[i]=pos[i]-pos[i-1];
        for(int i=1;i<=m;i++)pos[i]=pos[i-1]+d[i],stone[pos[i]]++;ll=pos[m];
        for(int i=1;i<=ll+t;i++)f[i]=m;
        for(int i=1;i<=ll+t;i++){
            for(int j=s;j<=t;j++){
                if(i-j>=0)f[i]=min(f[i],f[i-j]);
            }f[i]+=stone[i];
        }
        for(int i=ll;i<=ll+t;i++)ans=min(ans,f[i]);
        printf("%d
    ",ans);
        return 0;
    }

    T3

    篝火晚会

    题目描述

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

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

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

    这里m的值是由佳佳决定的,每次命令m的值都可以不同。这个命令的作用是移动编号是b1,b2,…… bm的这m个同学的位置。要求b1换到b2的位置上,b2换到b3的位置上,……,要求bm换到b1的位置上。执行每个命令都需要一些代价。我们假定如果一个命令要移动m个人的位置,那么这个命令的代价就是m。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?

    输入输出格式

    输入格式:

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

    输出格式:

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

    输入输出样例

    输入样例#1:
    4
    
    3 4
    
    4 3
    
    1 2
    
    1 2
    
    输出样例#1:
    2

    说明

    对于30%的数据,n <= 1000;

    对于全部的数据,n <= 50000。

    2005提高组第三题

     题解

    woc神题 做了好久...

    主要做法应该是贪心+乱搞....

    首先题目一定要读好啊,是任意m个点进行环形交换位置。所以1个点

    交换位置的代价是1。我们可以构造出满足要求的环,然后让那些已经在

    他本应该的位置上的那些人就不要动了,把那些不满足的从之前一堆人进

    行调换。这是我们的问题就变成了两个环,一个为一开始的坐法,一个是

    满足要求的做法怎样让这两个链镶嵌之后相同位置元素相同的个数最多呢

    不能摁着一个另一个转吧,正解是转x个格到自己应该在的位置的人,它转

    的格子是相同的....看代码...

    代码

    #include<iostream>
    #include<cstring>
    #include<cstdio> 
    using namespace std;
    
    int n,ans=1e9,a[50007],b[50007],f[50007],t[50007];
    
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]);
        t[1]=1;t[2]=a[1];
        for(int i=2;i<n;i++)
        if(t[i-1]==a[t[i]])t[i+1]=b[t[i]];
        else if(t[i-1]==b[t[i]])t[i+1]=a[t[i]];
        else {printf("-1");return 0;}
        for(int i=1;i<=n;i++)
        f[(t[i]-i)>=0?(t[i]-i):(t[i]+n-i)]++;
        for(int i=1;i<=n;i++)ans=min(ans,n-f[i]);
        memset(t,0,sizeof(t));memset(f,0,sizeof(f));
        t[1]=1;t[2]=b[1];
        for(int i=2;i<n;i++)
        if(t[i-1]==b[t[i]])t[i+1]=a[t[i]];
        else if(t[i-1]==a[t[i]])t[i+1]=b[t[i]];
        for(int i=1;i<=n;i++)
        f[(t[i]-i)>0?(t[i]-i):(t[i]+n-i)]++;
        for(int i=1;i<=n;i++)
        ans=min(ans,n-f[i]);
        printf("%d
    ",ans);
        return 0;
    }

    T4。。。。没做完...orz

  • 相关阅读:
    那么这几天都是自己和工作上的事情比较零零散散
    在大家都返现的时候却有人什么都不奉献
    今天觉得自己好像比较紧张
    今天是下午有雨
    今天是星期五,上班已经三个礼拜了
    今天晚上控制电脑前,要提前开机
    其实对于家里的电脑就是硬盘不太好
    家里主要忙着建立房子
    今天装了一个RTI工具
    昨天已经开始讲业务的部分
  • 原文地址:https://www.cnblogs.com/zzyh/p/7421885.html
Copyright © 2020-2023  润新知