• NOIP2011提高组day1解题报告【2017.3.24更新】


    同学大神写的题解

    更新日志

    • 2017-3-15补充了第三题的题解
    • 2017-3-18修正了第二题AC代码的一个致命错误
    • 2017-3-18更新了页面排版
    • 2017-3-24更新了更新日志 的排版

    第一题

    首先来看题目:

    为了准备一个独特的颁奖典礼,组织者在会场的一片 矩形区域(可看做 是平面直角坐标系的第一象限)铺上一些矩形地毯。一共有n 张地毯,编 号从1 到n。现在将这些地毯按照编号从小到大的顺序平行于坐标轴先后 铺设,后铺的地毯覆盖在前面已经铺好的地毯之上。地毯铺设完成后,组 织者想知道覆盖地面某个点的最上面的那张地毯的编号。注意:在矩形地 毯边界和四个顶点上的点也算被地毯覆盖。

    输入描述 Input Description
    输入共 n+2 行。
    第一行,一个整数 n,表示总共有n 张地毯。 接下来的 n 行中,第i+1 行表示编号i 的地毯的信息,包含四个正整数 a,b,g,k,每两个整数之间用一个空格隔开,分别表示铺设地毯的左下 角的坐标(a,b)以及地毯在x轴和y 轴方向的长度。 第 n+2 行包含两个正整数x 和y,表示所求的地面的点的坐标(x,y)。
    输出描述 Output Description
    输出共 1 行,一个整数,表示所求的地毯的编号;若此处没有被地毯覆盖 则输出-1。
    样例输入 Sample Input
    样例1:
    3
    1 0 2 3
    0 2 3 3
    2 1 3 3
    2 2

    样例2:
    3
    1 0 2 3
    0 2 3 3
    2 1 3 3
    4 5

    样例输出 Sample Output
    样例1:
    3

    样例2:
    -1

    数据范围及提示 Data Size & Hint
    数据范围
    对于 30%的数据,有n≤2;
    对于 50%的数据,0≤a, b, g, k≤100;
    对于 100%的数据,有0≤n≤10,000,0≤a, b, g, k≤100,000。

    一道很水的题,刚开始的时候想模拟地毯的铺放来解决,RE掉四个点,后来 发现根本不需要这么做,直接判断就好了,这里两种代码都附上。

    //这是模拟的代码,因为用了二维数组,空间开不了多大,会提示“too large”
    //开小了又会RE,这里只是用来辅助大家理解题意。
    #include <cstdio>
    #include <iostream>
    using namespace std;
    
    int n, a, b, g, k, x, y;
    int map[7010][7010];
    
    template<class T>inline void read(T &res)
    {
        static char ch;
        while( (ch=getchar()) < '0' || ch > '9');
            res = ch - 48;
        while( (ch = getchar() ) >= '0' && ch <= '9')
            res = ch - 48 + res * 10;
    }
    
    int main()
    {
        freopen("carpet.in", "r", stdin);
        freopen("carpet.out", "w", stdout);
        read(n);
        for (int i = 1; i <= n; i++)
        {
            read(a);read(b);read(g);read(k);
            for (int l = b; l <= b+k; l++)
                for (int j = a; j <= a+g; j++)
                    map[l][j] = i;
        }
        read(x); read(y);
        if (map[y][x] != 0) cout << map[y][x];
        else cout << -1;
        return 0;
    }
    //第二种直接判断的代码,时空复杂度都相当优。 
    #include <stdio.h>
    #include <iostream>
    #include <stdlib.h>
    using namespace std;
    
    int i, j, k, n, x, y;
    int ans[10005][5] = {0};
    
    template<class T>inline void read(T &res)
    {
        static char ch;
        while( (ch=getchar()) < '0' || ch > '9');
            res = ch - 48;
        while( (ch = getchar() ) >= '0' && ch <= '9')
            res = ch - 48 + res * 10;
    }
    
    int main()
    {
        read(n);
        for(i = 1; i <= n; i++)
            for(j = 1; j <= 4; j++)
                read(ans[i][j]);
        read(x);read(y);
        for(i = n; i >= 1; i--)
            if( ( (ans[i][1] + ans[i][3] ) >= x) && ( (ans[i][2] + ans[i][4] ) >= y ) && ans[i][1] <= x && ans[i][2] <= y )
            {
                cout << i;
                return 0;
            }
        cout << -1;
        return 0;
    }

     以下是评测截图。

    第二题

    依旧是先上题目

    丽江河边有 n 家很有特色的客栈,客栈按照其位置顺序从1 到n编号。 每家客栈都按照某一种色调进行装饰(总共k 种,用整数0~k-1表示) 且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费。     两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈 因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店 喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈)且 咖啡店的最低消费不超过p。     他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费 不超过p元的咖啡店小聚。

    输入描述 Input Description
         共n+1 行。
         第一行三个整数 n,k,p,每两个整数之间用一个空格隔开,分别表示客栈 的个数,色调的数目和能接受的最低消费的最高值;接下来的 n 行,第i+1 行两个整数,之间用一个空格隔开,分别表示i 号客栈的装饰色调和i 号客 栈的咖啡店的最低消费

    输出描述 Output Description
         输出只有一行,一个整数,表示可选的住宿方案的总数。

    样例输入 Sample Input
    5 2 3
    0 5
    1 3
    0 2
    1 4
    1 5

    样例输出 Sample Output
    3

    数据范围
    对于 30%的数据,有n≤100;
    对于 50%的数据,有n≤1,000;
    对于 100%的数据,有2≤n≤200,000,0<k≤50,0≤p≤100, 0≤最低消费≤100。

    先分析题目,很明显可以暴力(手动滑稽),暴力的思路就是直接枚举,从第一家客栈开始找,找到颜色相符合的就去枚举价格,如果价格符合要求(≤p)就ans++,不过很明显,会T掉。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    
    int n, k, p, ans;
    int t = 1;
    int color[200010];
    int house[200010];
    
    template<class T>inline void read(T &res)
    {
        static char ch;
        while( (ch=getchar()) < '0' || ch > '9');
            res = ch - 48;
        while( (ch = getchar() ) >= '0' && ch <= '9')
            res = ch - 48 + res * 10;
    }
    
    int DFS()
    {
        int mi;
        for (register int i = 1; i <= n; i++)
        {
            for (register int j = t+1; j <= n; j++)
            {
                if (color[j] == color[t])
                {
                    mi = 233333333;
                    for (int e = t; e <= j; e++)
                    {
                        if (house[e] < mi)
                        {
                            mi = house[e];
                        }
                    }
                    if (mi <= p) ans++;
                }
            }
            t++;
        }
        return ans;
    }
    
    int main()
    {
        read(n);read(k);read(p);
        for (register int i = 1; i <= n; i++)
        {
            read(color[i]);
            read(house[i]);
        }
        cout << DFS();
        return 0;
    }

    可以很清晰的看见这个做法有多么简单粗暴,但是就算是加了register一样会超时,超四个点,那么正解是什么呢?

    如果一家编号为i咖啡店的最低消费大于p,则记flag[i]=1否则,flag[i]=0但计算满足要求的对数有点恼火,从反面思考:从1到n扫描出flag值等于1的连续客栈(在此区间内找到的客栈均为不满足要求的方案),在检测时用cnt[j]记录当前颜色为j的客栈出现次数cnt[j]++;count+=(cnt [j]-1); (新的客栈可以与当前任意颜色相同客栈组合)可以数出颜色相同但不满足要求的客栈对数。答案ans=sum-count.(sum为所有颜色相同的客栈对数)即为满足要求的客栈对数。

    直接贴代码。

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    int Count, sum, n, k, p, tmp;
    int cnt[100];
    int col[200010];
    bool flag[200010];
    
    int main()
    {
        scanf("%d%d%d", &n, &k, &p);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &col[i]);
            scanf("%d", &tmp);
            if(tmp > p) flag[i] = 1;
        }
        for(int i = 1; i <= n; i++)
        {
            if(flag[i] == 0)
            {
                memset(cnt, 0, sizeof(cnt));
                continue;
            }
            cnt[col[i]]++;
            Count += (cnt[col[i]] - 1);
        }
        memset(cnt, 0, sizeof(cnt));
        for(int i = 1; i <= n; i++)
        {
            cnt[col[i]]++;
            sum += (cnt[col[i]] - 1);
        }
        cout << sum-Count;
        return 0;
    }

    第三题

    Top↑

    Mayan puzzle 是最近流行起来的一个游戏。 游戏界面是一个7 行5 列的棋盘,上面堆放 着一些方块,方 块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块 之上。游 戏通关是指在规定的步数内消除所有的方块,消除方块 的规则如下:

    1、每步移动可以且仅可以沿横向(即向左或向右)拖动某一方块 一格:当拖动这一方 块时,如果拖动后到达的位置(以下称目标 位置)也有方块,那么这两个方块将交换位置(参 见输入输出样 例说明中的图6 到图7);如果目标位置上没有方块,那么被拖动 的方块将从 原来的竖列中抽出,并从目标位置上掉落

    2、任一时刻,如果在一横行或者竖列上有连续三个或者三个以上 相同颜色的方块,则 它们将立即被消除(参见图1 到图3)。注意:
    a) 如果同时有多组方块满足消除条件,几组方块会同时被消除( 例如下面图4,三个颜 色为1 的方块和三个颜色为2 的方块会同 时被消除,最后剩下一个颜色为2 的方块)。
    b) 当出现行和列都满足消除条件且行列共享某个方块时,行和列 上满足消除条件的所 有方块会被同时消除(例如下面图5 所示的 情形,5 个方块会同时被消除)。

    3、方块消除之后,消除位置之上的方块将掉落,掉落后可能会引 起新的方块消除。注 意:掉落的过程中将不会有方块的消除。 上 面图 1 到图3 给出了在棋盘上移动一块方块之后棋盘的变化。棋 盘的左下角方块的坐 标为(0, 0),将位于(3, 3)的方块向左 移动之后,游戏界面从图1 变成图2 所示的状态, 此时在一竖列上 有连续三块颜色为4 的方块,满足消除条件,消除连续3 块颜色为4 的方块 后,上方的颜色为3 的方块掉落,形成图3 所示的局面。

    【输入描述】
    输入共六行。
    第一行输入一个正整数n,表示要求游戏通关的步数; 接下来五行,描述了7*5的游戏界面,每行输入若干个整数,每行以一 个0 结束,自下向上表示每竖列方块的颜色编号(颜色不多于10种,从 1开始顺序编号,相同数字表示相同颜色)。 保证初始棋盘中没有可以消除的方块。

    【输出描述】
    如果有解决方案,输出n行,每行包含三个整数x、y、g,表示一次移 动,其中(x,y)表示要移动的方块的坐标,g表示移动的方向,1表示 向右移动,-1表示向左移动。当存在多组解时,按照x为第一关健字, y为第二关健字,1优先于-1,输出一组字典序最小的解。游戏界面左 下角的坐标为(0,0);

    如果没有解决方案,输出一个整数-1。

    【输入样例】
    3
    1 0
    2 1 0
    2 3 4 0
    3 1 0
    2 4 3 4 0

    【输出样例】
    2 1 1
    3 1 1
    3 0 1

     据坊间传闻这是NOIP历年来最难得一道搜索,对个人而言确实还是有难度。

    其实主要难在删除块儿和下落,还有具体的优化,下面上代码,代码上都有批注。

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    int n;
    int Num[15], i[6][8], Ans[6][3];
    
    bool DEL(int i[6][8]) //清除
    {
        bool Flag = 0, f[6][8]={0};
        for (int a = 1; a <= 5; a++)
          for (int b = 1; b <= 7; b++)
            if (i[a][b])
            {
                if (a <= 3 && i[a][b] == i[a+1][b] && i[a+1][b] == i[a+2][b])
                  f[a][b] = f[a+1][b] = f[a+2][b] = true;
                if (b <= 5 && i[a][b] == i[a][b+1] && i[a][b+1] == i[a][b+2])
                  f[a][b] = f[a][b+1] = f[a][b+2] = true;
            }
        for (int a = 1; a <= 5; a++)
          for (int b = 1; b <= 7; b++)
            if (f[a][b])
            {
                i[a][b] = 0;
                Flag = true;
            }
        return Flag;
    }
    
    void Falldown(int i[6][8]) //下落
    {
        for (int a = 1; a <= 5; a++) //填补
        {
            int t = 0;
            for (int b = 1; b <= 7; b++)
            {
                int T = i[a][b];
                i[a][b] = 0;
                if (T)
                  i[a][++t] = T;
            }
        }
    }
    
    bool Check(int i[6][8]) //检验
    {
        for (int a = 1; a <= 5; a++)
          for (int b = 1; b <= 7; b++)
            if (i[a][b])
            return false;
        return true;
    }
    
    void DFS(int T, int i[6][8]) //DFS
    {
        if (T > n) //超步子 
        {
            if (Check(i))
            {
                for (int a = 1; a <= n; a++)
                  if (Ans[a][2]) //判断左右
                    printf("%d %d -1
    ", Ans[a][0], Ans[a][1]-1);
                  else
                    printf("%d %d 1
    ", Ans[a][0]-1, Ans[a][1]-1);
                exit(0);//结束 
            }
            return;
        }
        memset(Num, 0, sizeof(Num)); //存储某种颜色格子现存数
        for (int a = 1; a <= 5; a++)
          for (int b = 1; b <= 7; b++)
            if (i[a][b])
              Num[i[a][b]]++;
        for (int a = 1; a <= 10; a++)
          if (Num[a] == 1 || Num[a] == 2) //等于0时并不需要返回
            return;
        int f[6][8] = {0}; //临时数组
        for (int a = 1; a < 5; a++)
          for (int b = 1; b <= 7; b++)
            if (i[a][b] != i[a+1][b]) //颜色不一样才交换 
            {
                memcpy(f, i, sizeof(f));//复制值 
                Ans[T][0] = a;
                Ans[T][1] = b;
                Ans[T][2] =! i[a][b]; //若空,即是true,若有,即是false
                swap(f[a][b], f[a+1][b]); //交换
                Falldown(f);
                while (DEL(f)) //消除
                  Falldown(f);
                DFS(T+1, f);
            }
    }
    int main()
    {
        scanf("%d", &n);
        for (int a = 1; a <= 5; a++)
          for (int b = 1; ; b++)
          {
            scanf("%d", &i[a][b]); //i列j行 
            if (!i[a][b]) //存储颜色
              break;
          }
        DFS(1, i);
        printf("-1");//执行至此如果没有结束则输出-1 
        return 0;
    }
  • 相关阅读:
    array.prototype.slice.call(arguments)
    axios 的坑
    Cannot read property 'range' of null
    IDEA导入Eclipse的JavaEE项目详细步骤链接
    使用 yarn 安装时,报错node_modules ode sass:Command failed.
    axios post请求非json传参设置
    vue-cli 3.x跨域配置
    idea错误: 找不到或无法加载主类
    Git 上传新项目
    Windows下生成SSH密钥
  • 原文地址:https://www.cnblogs.com/GuanHuaEdison/p/6535478.html
Copyright © 2020-2023  润新知