[NOIP2011] day1铺地毯,选择客栈,Mayan游戏

这次考试有一些失误,首先我把第一道题做错了,说实话,这是这几次考试中最简单的第一道题,以至于我两分钟就打完了,但为什么会打错呢?因为我就看了一下样例,以为输出的是被地毯盖住的次数,估计只对了0的点,所以太着急是不行的
第二题我犯了两个错误,第一个是忽略了一种情况,认为后面的次数是前面的加一,但没想到可能能够取到前面所有的情况,输出值偏小,但改一下是行的,然而并不怎么能过所有的点,内存会爆而且O(kn)的时间也很长,1000万次再加上杂七杂八的运算肯定会挂。
第三题我是知道用dfs爆搜的,按字典序去搜,肯定结果是最优的,而且我也想到了标程的思路,但没编完就不想写了,尤其是小方块那一段写的尤其复杂,还不知道对不对,但看一看一些程序,他们的那些过程都很简洁,我还是要多学习一点

铺地毯

题目描述 Description
为了准备一个独特的颁奖典礼,组织者在会场的一片矩形区域(可看做是平面直角坐标系的第一象限)铺上一些矩形地毯。一共有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。

这道题讲真,估计没人会搜这道题的题解

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int MX = 100005;
int a[MX],b[MX],g[MX],k[MX];
int T,x,y,ans = 0;
int main() {
    freopen("carpet.in","r",stdin);
    freopen("carpet.out","w",stdout);
    scanf("%d", &T);
    for(int i = 1; i <= T; i++ ){
        scanf("%d%d%d%d", &a[i], &b[i], &g[i], &k[i]);
    }
    scanf("%d%d", &x, &y);
    for(int i = T;i >= 1; i-- )
        if(a[i] <= x && b[i] <= y && a[i] + g[i] >= x && b[i] + k[i] >= y){
            ans = i;break;
        }
    if( ans == 0 ) printf("-1");
    else printf("%d", ans);
    return 0;
}

选择客栈

题目描述 Description 
丽江河边有 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
数据范围及提示 Data Size & Hint 
【输入输出样例说明】
客栈编号
①
②
③
④
⑤
色调 
0 
1 
0 
1 
1 
最低消费 
5 
3 
2 
4 
5
2 人要住同样色调的客栈,所有可选的住宿方案包括:住客栈①③,②④,②⑤,④⑤,
但是若选择住 4、5 号客栈的话,4、5 号客栈之间的咖啡店的最低消费是 4,而两人能承受
的最低消费是 3 元,所以不满足要求。因此只有前 3 种方案可选。

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


这道题一看就知道暴力怎么写吧,建议尝试先写n方暴力,写着写着就明白了正解的思路了,因为你会知道搜每一点时更本不用去搜完前面所有点,并且不用每一个点都更新所有颜色,于是正解就诞生了。
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,k,p;
int now,clo,pri,ans;
int bef[55],ava[55],tot[55];//颜色
int main() {
    freopen("hotel.in","r",stdin);
    freopen("hotel.out","w",stdout);
    scanf("%d%d%d", &n, &k, &p);
    for(int i = 1;i <= n; i++ ) {
        scanf("%d%d", &clo, &pri);
        if(pri <= p) now = i;//更新最近可行点
        if(bef[clo] <= now) ava[clo] = tot[clo];//最新总数
        ans +=ava[clo];
        tot[clo]++;
        bef[clo] = i;
    }
    printf("%d",ans);
    return 0;
}

Mayan游戏

题目描述 Description 
    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 所示的局面。
【输入输出样例说明】

样例输入的游戏局面依次移动的三步是:(2,1)处的方格向
右移动,(3,1)处的方格向右移动,(3,0)处的方格向右移动,最后可以将棋盘上所有方
块消除。
输入描述 Input Description 
共6 行。
第一行为一个正整数 n,表示要求游戏通关的步数。
接下来的 5 行,描述7*5 的游戏界面。每行若干个整数,每两个整数之间用一个空格隔开,每行以一个0 结束,自下向上表示每竖列方块的颜色编号(颜色不多于10 种,从1 开始顺序编号,相同数字表示相同颜色)。
输入数据保证初始棋盘中没有可以消除的方块。
输出描述 Output Description 
如果有解决方案,输出n 行,每行包含3 个整数x,y,g,表示一次移动,每两个整数之间用一个空格隔开,其中(x,y)表示要移动的方块的坐标,g 表示移动的方向,1 表示向右移动,-1 表示向左移动。注意:多组解时,按照x 为第一关健字,y 为第二关健字,1优先于-1,给出一组字典序最小的解。游戏界面左下角的坐标为(0,0)。
如果没有解决方案,输出一行,包含一个整数-1。
样例输入 Sample Input 
3
1 0
2 1 0
2 3 4 0
3 1 0
2 4 3 4 0
样例输出 Sample Output 
2 1 1
3 1 1
3 0 1
这题就是dfs,但有优化,比如有相同方块是不用移动的,向右和向左效果相同时优先选择向左,只有左边是空的时候才向左移动,按字典序查找啊。消方块的难度也不比dfs小,因此这道题需要有优秀的代码实现能力,写暴力不一定比正解简单,exit()很好用啊建议用一下
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int MX = 200002;
int n,ak,a[6][8],num[7],x[6],y[6],g[6],tot;

void print() {
//输出函数
    for(int i = 1; i <= n; i++ )
        printf("%d %d %d\n",x[i],y[i],g[i]);
    exit(0);
}

int cle() {
//清方块并计数
    bool c[6][8];int ret = 0;
    memset(c,1,sizeof(c));
    for(int i = 0; i < 5; i++ )
        for(int j = 0; j < 7; j++ ) 
        if(a[i][j] && ((j && a[i][j-1]) || !j)) {
            int le = i,ri = i,up = j, dn = j;
            while(a[ri+1][j] == a[i][j] && ri < 4) ri++;
            while(a[le-1][j] == a[i][j] && le) le--;
            while(a[i][up+1] == a[i][j] && up < 6) up++;
            while(a[i][dn-1] == a[i][j] && dn) dn--;
            if(ri - le >= 2)
                for(int i1 = le; i1 <= ri; i1++ ) c[i1][j] = 0;
            if(up - dn >= 2)
                for(int j1 = dn; j1 <= up; j1++ ) c[i][j1] = 0;
        }
    for(int i = 0; i < 5; i++ )
        for(int j = 0; j < 7; j++)
            if(!c[i][j]) a[i][j] = 0,ret++;
//这里用了另一个数组存应该被消掉 的点
    bool flagg = 0;
    for(int i = 0; i < 5; i++ ) {
//让连续的一坨方块掉下来
        int j;
        for(j = 0; j < 7; j++)
            if(!a[i][j]) break;
        int lj = j;
        for(; j < 7; j++ )
            if(a[i][j]) break;
        if(j == 7) continue;
        for(int k = j; k < 7; k++ )
            if(a[i][lj+k-j] || !a[i][k]) break;
            else {
                a[i][lj+k-j] = a[i][k];a[i][k] = 0;flagg = 1;
            }
    }
    if(!flagg && !ret) return 0;
    ret +=cle();
    return ret;
}

void dfs(int u, int v) {
    if(u == n) {
        if(!v) print();
        return ;
    }
    u++;
    int b[6][8];
    for(int i = 0; i < 5; i++ )
        for(int j = 0; j < 7; j++ )
            b[i][j] = a[i][j];
//备用数组,保存a的值
    for(int i = 0; i < 5; i++ )
        for(int j = 0; j < 7 && a[i][j]; j++ ) {
            if(i != 4 && a[i][j] != a[i+1][j]) {
//向右不能是最右的点,不相同
                x[u] = i;y[u] = j;g[u] = 1;
                swap(a[i][j],a[i+1][j]);
                int now = v - cle();dfs(u,now);
                for(int df = 0; df < 5; df++ )
                    for(int ds = 0; ds < 7; ds++ )
                        a[df][ds] = b[df][ds];
            }
            if( !a[i-1][j] && i != 0) {
//向左不为0
                x[u] = i,y[u] = j;g[u] = -1;
                swap(a[i-1][j],a[i][j]);
                int now = v - cle();dfs(u,now);
                for(int df = 0; df < 5; df++ )
                    for(int ds = 0; ds < 7; ds++ )
                        a[df][ds] = b[df][ds];
            }
        }
}
int main() {
    freopen("mayan.in","r",stdin);
    freopen("mayan.out","w",stdout);
    scanf("%d", &n);
    for(int i = 0; i < 5; i++ ) while(scanf("%d", &ak) && ak) a[i][num[i]++] = ak,tot++;
    dfs(0,tot);printf("-1");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值