同学大神写的题解
更新日志
- 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; }