NOIP 2011 题解 铺地毯 选择客栈 Mayan 游戏
2017-03-11 14:31
447 查看
大家都很强,可与之共勉
大白兔的奶糖’s T 解
T1
1.铺地毯(carpet.cpp/c/pas)
【问题描述】
为了准备一个独特的颁奖典礼,组织者在会场的一片矩形区域(可看做是平面直角坐标
系的第一象限)铺上一些矩形地毯。一共有n 张地毯,编号从1 到n。现在将这些地毯按照
编号从小到大的顺序平行于坐标轴先后铺设,后铺的地毯覆盖在前面已经铺好的地毯之上。
地毯铺设完成后,组织者想知道覆盖地面某个点的最上面的那张地毯的编号。注意:在矩形
地毯边界和四个顶点上的点也算被地毯覆盖。
【输入】
输入文件名为 carpet.in。
输入共 n+2 行。
第一行,一个整数 n,表示总共有n 张地毯。
接下来的 n 行中,第i+1 行表示编号i 的地毯的信息,包含四个正整数a,b,g,k,每
两个整数之间用一个空格隔开,分别表示铺设地毯的左下角的坐标(a,b)以及地毯在x
轴和y 轴方向的长度。
第 n+2 行包含两个正整数x 和y,表示所求的地面的点的坐标(x,y)。
【输出】
输出文件名为 carpet.out。
输出共 1 行,一个整数,表示所求的地毯的编号;若此处没有被地毯覆盖则输出-1。
【输入输出样例 1】
carpet.in
3
1 0 2 3
0 2 3 3
2 1 3 3
2 2
carpet.out
3
【输入输出样例 2】
carpet.in
3
1 0 2 3
0 2 3 3
2 1 3 3
4 5
carpet.out
-1
【数据范围】
对于 30%的数据,有n≤2;
对于 50%的数据,0≤a, b, g, k≤100;
对于 100%的数据,有0≤n≤10,000,0≤a, b, g, k≤100,000。
T解:
这一题乍一看,嘿呀,让我想起了刚刚学信息竞赛的时候的校门外的树用for()来覆盖区间的问题,一定建一个图,然后一看数据范围0≤n≤10,000,0≤a, b, g, k≤100,000。可能是要for()到候年马月了。然而在读题:编号从小到大的顺序平行于坐标轴先后铺设,后铺的地毯覆盖在前面已经铺好的地毯之上。地毯铺设完成后,组织者想知道覆盖地面某个点的最上面的那张地毯的编号。所以既然只要知道那一个点是属于哪一个地毯,我们就不需要把每一点的归属情况找出来,直接特判就好。因为是编号从小到大的顺序铺设,所以for()的时候从n -> 1倒起来找,找到第一个符合的就是答案,输出即可。如果从n -> 1没有找到,就输出无解的情况(-1)。
代码如下:
#include "cstdio" #include "cctype" template <class T> inline bool readIn( T &x ) { x = 0; T flag = 1; char ch = (char) getchar(); while( !isdigit(ch) ) { if( ch == '-' ) flag = -1; ch = (char) getchar(); } while( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ch - 48, ch = (char) getchar(); } x *= flag; } template <class T> inline void writeIn( T x ) { if( x < 0 ) { x = -x; putchar('-'); } if( x > 9 ) writeIn( x / 10 ); putchar(x % 10 + 48); } const int MAXN = 10005; int n, x, y; struct matrix { int a, b, g, k; } m[MAXN]; int main() { freopen("carpet.in", "r", stdin); freopen("carpet.out", "w", stdout); readIn(n); for( register int i = 1; i <= n; ++i ) readIn(m[i].a),readIn(m[i].b),readIn(m[i].g),readIn(m[i].k); readIn(x);readIn(y); for( register int i = n; i >= 1; --i ) if( x >= m[i].a && x <= m[i].a + m[i].g && y >= m[i].b && y <= m[i].b + m[i].k ) { writeIn(i); putchar('\n'); return 0; } writeIn(-1); putchar('\n'); return 0; }
T2
2.选择客栈(hotel.cpp/c/pas)
【问题描述】
丽江河边有 n 家很有特色的客栈,客栈按照其位置顺序从1 到n 编号。每家客栈都按照
某一种色调进行装饰(总共k 种,用整数0 ~ k-1 表示),且每家客栈都设有一家咖啡店,每
家咖啡店均有各自的最低消费。
两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定
分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店喝咖啡,要求咖啡店位于
两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过p。
他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过p
元的咖啡店小聚。
【输入】
输入文件 hotel.in,共n+1 行。
第一行三个整数 n,k,p,每两个整数之间用一个空格隔开,分别表示客栈的个数,色
调的数目和能接受的最低消费的最高值;
接下来的 n 行,第i+1 行两个整数,之间用一个空格隔开,分别表示i 号客栈的装饰色
调和i 号客栈的咖啡店的最低消费。
【输出】
输出文件名为 hotel.out。
输出只有一行,一个整数,表示可选的住宿方案的总数。
【输入输出样例 1】
hotel.in
5 2 3
0 5
1 3
0 2
1 4
1 5
hotel.out
3
【数据范围】
对于 30%的数据,有n≤100;
对于 50%的数据,有n≤1,000;
对于 100%的数据,有2≤n≤200,000,0<=k≤50,0≤p≤100, 0≤最低消费≤100。
我不会告诉你我最开始写了一个线段树,就是在扯淡嘛我去,要查询多少次。说不定还要TLE。回头一想,这道题可以用dfs(),不过看数据范围dfs()可能会TLE一个点。再仔细分析,其实其实这道题满足最优子结构,而且没有后效性。所以就是一道DP。我想转移方程想了近半个小时,其实不困难。主要是不好理解,而且辅助的数组较多。用col[i]表示颜色为i的客栈之前所有的颜色为i的个数(不包括该客栈),maxc[i]表示之前的颜色为i且编号最大的那一个客栈的编号。f[i]表示1-i中最大的最低消费不大于p的客栈的编号。g[i]表示i之前的色调与i号客栈相同的最大的客栈的编号。same[i]表示i之前与i的色调相同客栈的总数(其实可以不要的,可以直接用col[color],不过方便理解),sol[i]表示色调与i相同而且存在最低消费<=p的数目。(这里面的最大其实就是距离i号客栈最近的意思)。
所以转移方程有1mol多。
每次输入color和least;
g[i] = maxc[color];
if(least<= p) f[i] = i; else f[i] = f[i-1];
if( f[i] < g[i] ) sol[i] = sol[g[i]];
else sol[i] = col[color];
maxcol[color] = i;
++col[color];
主要应该解释if( f[i] < g[i] ) sol[i] = sol[g[i]]; else sol[i] = col[color],我觉得,自己想好了,是想得清楚的。
#include "cstdio" #include "cctype" template <class T> inline bool readIn( T &x ) { x = 0; T flag = 1; char ch = (char) getchar(); while( !isdigit(ch) ) { if( ch == '-' ) flag = -1; ch = (char) getchar(); } while( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ch - 48, ch = (char) getchar(); } x *= flag; } template <class T> inline void writeIn( T x ) { if( x < 0 ) { x = -x; putchar('-'); } if( x > 9 ) writeIn( x / 10 ); putchar(x % 10 + 48); } const int MAXN = 200005; int n, k, p, ans, color, leaco;; int col[MAXN], f[MAXN], g[MAXN], sol[MAXN], maxcol[MAXN]; int main() { freopen("hotel.in", "r", stdin); freopen("hotel.out", "w", stdout); readIn(n);readIn(k);readIn(p); for( register int i = 1; i <= n; ++i ) { readIn(color);readIn(leaco); g[i] = maxcol[color]; if(leaco <= p) f[i] = i; else f[i] = f[i-1]; if( f[i] < g[i] ) sol[i] = sol[g[i]]; else sol[i] = col[color]; maxcol[color] = i; ++col[color]; } for( register int i = 2; i <= n; ++i ) ans += sol[i]; writeIn(ans); putchar('\n'); return 0; }
T3
3.Mayan 游戏(mayan.cpp/c/pas)
【问题描述】
Mayan puzzle 是最近流行起来的一个游戏。游戏界面是一个7 行5 列的棋盘,上面堆放
着一些方块,方块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块之上。游
戏通关是指在规定的步数内消除所有的方块,消除方块的规则如下:
1、每步移动可以且仅可以沿横向(即向左或向右)拖动某一方块一格:当拖动这一方
块时,如果拖动后到达的位置(以下称目标位置)也有方块,那么这两个方块将交换位置(参
见输入输出样例说明中的图6 到图7);如果目标位置上没有方块,那么被拖动的方块将从
原来的竖列中抽出,并从目标位置上掉落(直到不悬空,参见下面图1 和图2);
【输入】
输入文件 mayan.in,共6 行。
第一行为一个正整数 n,表示要求游戏通关的步数。
接下来的 5 行,描述7*5 的游戏界面。每行若干个整数,每两个整数之间用一个空格隔
开,每行以一个0 结束,自下向上表示每竖列方块的颜色编号(颜色不多于10 种,从1 开
始顺序编号,相同数字表示相同颜色)。
输入数据保证初始棋盘中没有可以消除的方块。
【输出】
输出文件名为 mayan.out。
如果有解决方案,输出n 行,每行包含3 个整数x,y,g,表示一次移动,每两个整数
之间用一个空格隔开,其中(x,y)表示要移动的方块的坐标,g 表示移动的方向,1 表示
向右移动,-1 表示向左移动。注意:多组解时,按照x 为第一关健字,y 为第二关健字,1
优先于-1,给出一组字典序最小的解。游戏界面左下角的坐标为(0,0)。
如果没有解决方案,输出一行,包含一个整数-1。
【输入输出样例 1】
mayan.in
3
1 0
2 1 0
2 3 4 0
3 1 0
2 4 3 4 0
mayan.out
2 1 1
3 1 1
3 0 1
样例输入的游戏局面如上面第一个图片所示,依次移动的三步是:(2,1)处的方格向
右移动,(3,1)处的方格向右移动,(3,0)处的方格向右移动,最后可以将棋盘上所有方
块消除。
【数据范围】
对于 30%的数据,初始棋盘上的方块都在棋盘的最下面一行;
对于 100%的数据,0 < n≤5。
#include "cstdio" #include "cctype" #include "cstdlib" #include "cstring" template <class T> inline bool readIn( T &x ) { x = 0; T flag = 1; char ch = (char) getchar(); while( !isdigit(ch) ) { if( ch == '-' ) flag = -1; ch = (char) getchar(); } while( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ch - 48, ch = (char) getchar(); } x *= flag; } int n, color[7][9], doit[6][3], cnt[20]; bool remove( int tmp[][9] ) { bool pos[7][9], flag = false; memset(pos, false, sizeof(pos)); for( int i = 1; i <= 5; ++i ) for( int j = 1; j <= 7; ++j ) if( tmp[i][j] ) { if( i <= 3 && tmp[i][j] == tmp[i + 1][j] && tmp[i + 1][j] == tmp[i + 2][j] ) pos[i][j] = pos[i + 1][j] = pos[i + 2][j] = true; if( j <= 5 && tmp[i][j] == tmp[i][j + 1] && tmp[i][j + 1] == tmp[i][j + 2]) pos[i][j] = pos[i][j + 1] = pos[i][j + 2] = true; } for( int i = 1; i <= 5; ++i ) for( int j = 1; j <= 7; ++j ) if(pos[i][j]) tmp[i][j] = 0, flag = 1; return flag; } inline bool fall( int tmp[][9] ) { int k, t; for( int i = 1; i <= 5; ++i ) { k = 0; for( int j = 1; j <= 7; ++j ) { t = tmp[i][j], tmp[i][j] = 0; if(t) tmp[i][++k] = t; } } } bool isclear(int tmp[][9]) { for( int i = 1; i <= 5; ++i ) for( int j = 1; j <= 7; ++j ) if( tmp[i][j] ) return false; return true; } void dfs( int step, int tmp[][9] ) { if( step > n ) { if( isclear(tmp) ) { for( int i = 1; i <= n; ++i ) { if(doit[i][2]) printf("%d %d -1\n", doit[i][0], doit[i][1] - 1); // i + 1 -> i; else printf("%d %d 1\n", doit[i][0] - 1, doit[i][1] - 1); } exit(0); } return; } int use[7][9]; memset(use, 0, sizeof(use)); memset(cnt, 0, sizeof(cnt)); for( int i = 1; i <= 5; ++i ) for( int j = 1; j <= 7; ++j ) ++cnt[tmp[i][j]]; for( int i = 1; i <= 10; ++i ) if( cnt[i] == 1 || cnt[i] == 2 ) return; for( int i = 1; i < 5; ++i ) for( int j = 1; j <= 7; ++j ) if( tmp[i][j] - tmp[i + 1][j] ) { //for( int k = 1; k <= 5; ++k ) //for( int l = 1; l <= 7; ++l ) //use[k][l] = tmp[k][l]; memcpy(use, tmp, sizeof(use)); doit[step][0] = i; doit[step][1] = j; doit[step][2] = !tmp[i][j]; use[i][j] ^= use[i + 1][j]; use[i + 1][j] ^= use[i][j]; use[i][j] ^= use[i + 1][j]; fall(use); while(remove(use)) fall(use); dfs( step + 1, use); } } int main() { freopen("mayan.in", "r", stdin); freopen("mayan.out", "w", stdout); readIn(n); for(int i = 1, j = 0; i <= 5; ++i, j = 0) do readIn(color[i][++j]); while( color[i][j] ); dfs(1, color); puts("-1\n"); return 0; }
当然用memcpy()要快得多;
成长, 历练, 所向披靡
相关文章推荐
- [NOIP2011] day1铺地毯,选择客栈,Mayan游戏
- NOIP2011复赛提高组day1(A:铺地毯 B:选择客栈 C:mayan游戏)
- NOIP 2011 DAY 1 解题报告(铺地毯,选择客栈,mayan游戏)
- NOIP2011 铺地毯 选择客栈 Mayan游戏
- Noip 2011 解题报告 Day1 (铺地毯,选择客栈,Mayan 游戏)
- NOIP2011(DAY1)解题报告(C/C++)(铺地毯)(选择客栈)(Mayan 游戏)
- [NOIP2011]Mayan游戏 题解
- NOIP2011 Mayan游戏 题解
- NOIP2011 选择客栈 题解(最简方法,超短代码)
- 【NOIP 2011】 选择客栈
- 洛谷 P1312 [NOIP2011 D1T3] Mayan游戏
- [Wikioi 1135][NOIP 2011提高组]选择客栈(疑难题)
- NOIP 2011 提高组 选择客栈(vijos 1737)(方法:队列,数学)
- COGS 621.[NOIP2011] 选择客栈 解题报告
- NOIP 2011 mayan游戏
- 【NOIP 2011】Mayan游戏
- [NOIP2011提高组day1]-3-mayan游戏
- NOIP2011选择客栈[递推]
- 【NOIP2011】【DFS】Mayan游戏
- NOIP2011 选择客栈