• 2014-5-16 NOIP模拟赛


    Problem 1 抓牛(catchcow.cpp/c/pas)

    【题目描述】

           农夫约翰被通知,他的一只奶牛逃逸了!所以他决定,马上出发,尽快把那只奶牛抓回来.

    他们都站在数轴上.约翰在N(O≤N≤100000)处,奶牛在K(O≤K≤100000)处.约翰有两种办法移动,步行和瞬移:步行每秒种可以让约翰从x处走到x+lx-l处;而瞬移则可让他在1秒内从x处消失,在2x处出现.然而那只逃逸的奶牛,悲剧地没有发现自己的处境多么糟糕,正站在那儿一动不动.

           那么,约翰需要多少时间抓住那只牛呢?

    【输入格式】

    仅有两个整数NK

    【输出格式】

    最短时间

    【样例输入】

    5 17

    【样例输出】

    4

    /*
        宽搜,有点像spfa
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    #define maxn 100010
    int dis[maxn],N,K,mx;
    queue<int>q;
    bool vis[maxn];
    void bfs(){
        memset(dis,127/3,sizeof(dis));
        q.push(N);
        dis[N]=0;
        vis[N]=1;
        while(!q.empty()){
            int now=q.front();q.pop();
            if(now>0&&dis[now-1]>dis[now]+1){
                dis[now-1]=dis[now]+1;
                if(now-1==N)return;
                if(!vis[now-1]){
                    vis[now-1]=1;
                    q.push(now-1);
                }
            }
            if(now+1<=mx&&dis[now+1]>dis[now]+1){
                dis[now+1]=dis[now]+1;
                if(now+1==N)return;
                if(!vis[now+1]){
                    vis[now+1]=1;
                    q.push(now+1);
                }
            }
            if(now*2<=mx&&dis[now*2]>dis[now]+1){
                dis[now*2]=dis[now]+1;
                if(now*2==N)return;
                if(!vis[now*2]){
                    vis[now*2]=1;
                    q.push(now*2);
                }
            }
        }
    }
    int main(){
        freopen("catchcow.in","r",stdin);
        freopen("catchcow.out","w",stdout);
        scanf("%d%d",&N,&K);
        mx=max(N,K)+1;
        q.push(N);
        bfs();
        printf("%d",dis[K]);
    }

    Problem 2 路面修整(grading.cpp/c/pas)

    【题目描述】

    FJ打算好好修一下农场中某条凹凸不平的土路。按奶牛们的要求,修好后的路面高度应当单调上升或单调下降,也就是说,高度上升与高度下降的路段不能同时出现在修好的路中。 整条路被分成了N段,N个整数A_1, ... , A_N (1 <= N <= 2,000)依次描述了每一段路的高度(0 <= A_i <= 1,000,000,000)FJ希望找到一个恰好含N个元素的不上升或不下降序列B_1, ... , B_N,作为修过的路中每个路段的高度。由于将每一段路垫高或挖低一个单位的花费相同,修路的总支出可以表示为: |A_1 - B_1| + |A_2 - B_2| + ... + |A_N - B_N| 请你计算一下,FJ在这项工程上的最小支出是多少。FJ向你保证,这个支出不会超过2^31-1

    【输入格式】
     1: 输入1个整数:N * 2..N+1: i+1行为1个整数:A_i

    【输出格式】
    1: 输出1个正整数,表示FJ把路修成高度不上升或高度不下降的最小花费

    【样例输入】

    7
    1
    3
    2
    4
    5
    3
    9

    【样例输出】

    3

    【样例解释】

    FJ将第一个高度为3的路段的高度减少为2,将第二个高度为3的路段的高度增加到5,总花费为|2-3|+|5-3| = 3,并且各路段的高度为一个不下降序列 1,2,2,4,5,5,9

    /*
        很显然的dp,题目在拼命的让你想最长不下降和最长不上升子序列 
        可是,这个dp怎么做?
        我们很难确定最终结果是上升还是下降,但是数据规模才2000,n方! 
        两种结果都讨论一下。
        将数列存两遍,a[]为原序列,b[]是有序的序列 
        dp[i][j]是指已讨论到i,并把a[i]变成b[j]的最小代价 
        则转移方程为dp[i][j]=min(dp[i][j-1],dp[i-1][j]+abs(a[i]-b[j]))
    */
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<cstdlib>
    using namespace std;
    #define maxn 2010
    int n,a[maxn],b[maxn],dp[maxn][maxn],f[maxn][maxn],ans;
    void prepare(){
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)dp[i][0]=0x3fffffff;
        memset(f,0,sizeof(f));
    }
    int cmp(int x,int y){
        return x>y;
    }
    int main(){
        //freopen("Cola.txt","r",stdin);
        freopen("grading.in","r",stdin);
        freopen("grading.out","w",stdout);
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        sort(b+1,b+n+1);
        prepare();
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                f[i][j]=dp[i-1][j]+abs(a[i]-b[j]);
                dp[i][j]=min(dp[i][j-1],f[i][j]);
            }
        }
        ans=dp[n][n];
        prepare();
        sort(b+1,b+n+1,cmp);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                f[i][j]=dp[i-1][j]+abs(a[i]-b[j]);
                dp[i][j]=min(dp[i][j-1],f[i][j]);
            }
        }
        ans=min(dp[n][n],ans);
        printf("%d",ans);
    }

    Problem 3 教主的魔法(magic.cpp/c/pas)

    【题目描述】

    教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为12……N

    每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[L, R]1≤L≤R≤N)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第LR)个英雄的身高)

    CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。

    WD巨懒,于是他把这个回答的任务交给了你。

    【输入格式】

     1行为两个整数NQQ为问题数与教主的施法数总和。

     2行有N个正整数,第i个数代表第i个英雄的身高。

     3到第Q+2行每行有一个操作:

    1)若第一个字母为“M”,则紧接着有三个数字LRW。表示对闭区间 [L, R] 内所有英雄的身高加上W

    2)若第一个字母为“A”,则紧接着有三个数字LRC。询问闭区间 [L, R] 内有多少英雄的身高大于等于C

    【输出格式】

         对每个“A”询问输出一行,仅含一个整数,表示闭区间 [L, R] 内身高大于等于C的英雄数。

     【样例输入】

    5 3

    1 2 3 4 5

    A 1 5 4

    M 3 5 1

    A 1 5 4

    【样例输出】

    2

    3

    【数据范围】

    【输入输出样例说明】

    原先5个英雄身高为12345,此时[1, 5]间有2个英雄的身高大于等于4。教主施法后变为12456,此时[1, 5]间有3个英雄的身高大于等于4

    【数据范围】

    30%的数据,N≤1000Q≤1000

    100%的数据,N≤1000000Q≤30001≤W≤10001≤C≤1,000,000,000

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int n,m,cnt,a[1000010],b[1000010],pos[1000010],block,ADD[1010];
    void Set(int x){
        int l=(x-1)*block+1,r=min(n,x*block);
        for(int i=l;i<=r;i++)b[i]=a[i];
        sort(b+l,b+r+1);
    }
    void Grow(int x,int y,int z){
        if(pos[x]==pos[y])for(int i=x;i<=y;i++)a[i]+=z;
        else {
            for(int i=x;i<=pos[x]*block;i++)a[i]+=z;
            for(int i=(pos[y]-1)*block+1;i<=y;i++)a[i]+=z;
        }
        Set(pos[x]);Set(pos[y]);
        for(int i=pos[x]+1;i<pos[y];i++)ADD[i]+=z;
    }
    int coco(int x,int v){
        int l=(x-1)*block+1,r=min(n,x*block);int last=r;
        while(l<=r){
            int mid=(l+r)>>1;
            if(b[mid]<v)l=mid+1;
            else r=mid-1;
        }
        return last-l+1;
    }
    int Ask(int x,int y,int z){
        int ans=0;
        if(pos[x]==pos[y]){
            for(int i=x;i<=y;i++)if(a[i]+ADD[pos[i]]>=z)ans++;
        }
        else{
            for(int i=x;i<=pos[x]*block;i++)if(a[i]+ADD[pos[i]]>=z)ans++;
            for(int i=(pos[y]-1)*block+1;i<=y;i++)if(a[i]+ADD[pos[i]]>=z)ans++;
        }
        for(int i=pos[x]+1;i<pos[y];i++)ans+=coco(i,z-ADD[i]);
        return ans;
    }
    int qread(){
        char c=getchar();int i=0,j=1;
        while(c<'0'||c>'9'){if(c=='-')j=-1;c=getchar();}
        while(c<='9'&&c>='0'){i=i*10+c-'0';c=getchar();}
        return i*j;
    }
    int main(){
        freopen("magic.in","r",stdin);
        freopen("magic.out","w",stdout);
        n=qread();m=qread();
        block=(int)(sqrt(n));
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            pos[i]=(i-1)/block+1;
            b[i]=a[i];
        }
        if(n%block)cnt=n/block+1;
        else cnt=n/block;
        for(int i=1;i<=cnt;i++)Set(i);
        char ch[5];int x,y,z;
        for(int i=1;i<=m;i++){
            scanf("%s",ch);
            x=qread();y=qread();z=qread();
            if(ch[0]=='M')Grow(x,y,z);
            if(ch[0]=='A')printf("%d
    ",Ask(x,y,z));
        }
    }

    Problem 4 吃豆豆(pacman.cpp/c/pas)

    【问题描述】

             两个PACMAN吃豆豆。一开始的时候,PACMAN都在坐标原点的左下方,豆豆都在右上方。PACMAN走到豆豆处就会吃掉它。PACMAN行走的路线很奇怪,只能向右走或者向上走,他们行走的路线不可以相交。

             请你帮这两个PACMAN计算一下,他们两加起来最多能吃掉多少豆豆。

    【输入文件】

             第一行为一个整数N,表示豆豆的数目。接下来N行,每行一对正整数XiYi,表示第i个豆豆的坐标。任意两个豆豆的坐标都不会重合。

    【输出文件】

             仅有一行包含一个整数,即两个PACMAN加起来最多能吃掉的豆豆数量。

    【输入样例】

    8

    8  1

    1  5

    5  7

    2  2

    7  8

    4  6

    3  3

    6  4

    【输出样例】

    7

    【数据规模】

    对于30%的数据,1<=N<=25

    对于70%的数据,1<=N<=500

    对于100%的数据,1<=N<=2000,1<=Xi Yi <=200000  

  • 相关阅读:
    汇编语言-子程序调用
    汇编语言-转移指令的原理
    汇编语言-直接定址表
    汇编语言-内中断
    汇编语言-汇编程序初识
    【Mybtais】Mybatis 插件 Plugin开发(一)动态代理步步解析
    【Redis】redis异步消息队列+Spring自定义注解+AOP方式实现系统日志持久化
    【ECharts】报表联动,动态数据设计
    【】POST、GET、RequestParam、ReqestBody、FormData、request payLoad简单认知
    【TensorFlow】Win7下使用Object Detection API 训练自己的数据集,并视频实时检测
  • 原文地址:https://www.cnblogs.com/thmyl/p/7207869.html
Copyright © 2020-2023  润新知