NOIP2011 铺地毯 选择客栈 Mayan游戏
2017-03-11 17:45
357 查看
NOIP2011
第一题
很简单的题目,通过给定的条件可以求出矩形的左下角与右上角的坐标,进而判断是否覆盖。离线操作,顺序更新。
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int N = 10010; int a , b , x , y ; int main(){ freopen("carpet.in", "r", stdin); freopen("carpet.out", "w", stdout); int n, xx, yy; scanf("%d", &n); for(int i=1; i<=n; i++){ scanf("%d%d%d%d", &a[i],&b[i],&x[i],&y[i]); } scanf("%d%d", &xx, &yy); int ans = 0; for(int i=1; i<=n; i++){ if(a[i] <= xx && b[i] <= yy && a[i] + x[i] >= xx && b[i] + y[i] >= yy) ans = i; } if(ans == 0) printf("-1"); else printf("%d", ans); return 0; }
第二题
这道题算是一道dp题,不过有许多其他的解决方案,考试时当然选择最快捷的方案,所以我并没有选择dp,如果读者想要用dp做的话这里有dp的题解下面代码展示的方法是直接判断,只不过不是单纯的dfs,由于条件一是相同颜色,所以我们就把相同颜色的放入一个数组当中,利用单个数组进行判断。又把可以做咖啡店的地方标记出来放入一个数组当中,因为是遍输入遍操作,而且输入又是按照序号的,所以数组中都是有序的,通过lower_bound查找合法选择,再加一丢丢的dp思想,靠后的客栈合法,那么它之后的都合法。运气好是O(n),不好就是O(n*n)了,虽然不是最快的方法,但是对付这道题绰绰有余了。
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; int f[55][200010]; int h[55]; int main(){ freopen("hotel.in", "r", stdin); freopen("hotel.out", "w", stdout); int n, k, p; scanf("%d%d%d", &n,&k,&p); for(int i=1; i<=n; i++){ int col, pri; scanf("%d%d", &col, &pri); f[col][++h[col]] = i; if(pri <= p) f[51][++h[51]] = i; } f[51][h[51]+1] = 900000; long long ans = 0; for(int i=0; i<k; i++) for(int j=1; j<=h[i]; j++){ int cc = lower_bound(f[51]+1,f[51]+1+h[51],f[i][j]) - f[51];//满足条件的第一个位置 int dd = lower_bound(f[i]+1,f[i]+1+h[i],f[51][cc]) - f[i];//满足条件的第一个位置 if(dd == j) dd++;//不同客栈 ans += (h[i] - dd + 1); } printf("%I64d", ans); return 0; }
第三题
由于棋盘较小,又只有两个方向的移动选择,经鉴定,可以搜索。不过各种优化是万万少不了的。如果当前状态有一种颜色的数量小于3,那么这种颜色就无法被消除,于是return。如果两个连着的方块颜色是相同的话,不用交换。如果两个非空的方块交换,我们只用考虑左边那个方块右移(按字典序优先)。如果一个方块是空的,它的右边非空,只考虑它右边的方块左移,当枚举到它右边方块的时候也不需要再考虑左移的情况。要注意,下落时清除方块要反复多次,因为清除方块以后,下落形成的新图形可能又出现了能清除的方块。用memcpy保存上一步状态,便于return时还原。drop函数利用了类似映射的方法,降低了时间复杂度。
#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> using namespace std; struct ln{ int x,y,dir; }ans[10]; int mp[10][10]; int n; int sum[12]; bool empty(){ for(int i=0; i<5; i++) for(int j=0; j<7; j++) if(mp[i][j]) return false; return true; }//判空 void drop(){ int num[10][10]; memset(num,-1,sizeof num); for(int x=0; x<5; x++){ int h=0; for(int y=0; y<7; y++) if(mp[x][y]) num[x][h++] = y; } for(int x=0; x<5; x++) for(int y=0; y<7; y++) if(num[x][y] == -1) mp[x][y] = 0; else mp[x][y] = mp[x][num[x][y]]; return; }//映射 bool clear(){ bool flag = 0; for(int x=0; x<3; x++) for(int y=0; y<7; y++) if(mp[x][y]){ int x2; for(x2=x; x2+1<5 && mp[x2+1][y] == mp[x][y]; x2++); if(x2 - x >= 2){ int p; for(p = x; p <= x2; p++){ int Up = y, Dn = y; while(Up+1<7 && mp[p][Up+1] == mp[x][y]) Up++; while(Dn-1>=0 && mp[p][Dn-1] == mp[x][y]) Dn--; if(Up - Dn >= 2){ int q; for(q = Dn; q <= Up; q++) mp[p][q] = 0; } } for(p = x; p <= x2; p++) mp[p][y] = 0; flag = 1; } } for(int x=0; x<5; x++) for(int y=0; y<5; y++) if(mp[x][y]){ int y2; for(y2 = y; y2+1<7 && mp[x][y2+1] == mp[x][y]; y2++); if(y2 - y >= 2){ int q; for(q = y; q <= y2; q++){ int LL = x, RR = x; while(LL-1>=0 && mp[LL-1][q] == mp[x][y]) LL--; while(RR+1<7 && mp[RR+1][q] == mp[x][y]) RR++; if(RR - LL >= 2){ int p; for(p = LL; p <= RR; p++) mp[p][q] = 0; } } for(q = y; q <= y2; q++) mp[x][q] = 0; flag = 1; } } if(flag) return true; else return false; }//消除 void dfs(int step){ if(step > n){ if(empty()){ for(int i=1; i<=n; i++){ if(ans[i].dir) printf("%d %d %d\n",ans[i].x+1,ans[i].y,-1); //dir==1时->x+1左移 else printf("%d %d %d\n",ans[i].x,ans[i].y,1); } exit(0); } return; } for(int i=1; i<=10; i++)//优化 if(sum[i] != 0&&sum[i] < 3) return; for(int x=0; x<4; x++) for(int y=0; y<7; y++) if(mp[x][y] != mp[x+1][y]){ ans[step].x = x; ans[step].y = y; ans[step].dir = (!mp[x][y]);//如果当前这一块是空的话,就把它右边那一块左移 int test[10][10]; memcpy(test,mp,sizeof test);//保存前一个状态 swap(mp[x][y],mp[x+1][y]); drop(); while(clear()) drop();//下移之后还肯能产生新的合法联通块,因此写成循环的形式 dfs(step+1); ans[step].x = 0; ans[step].y = 0; ans[step].dir = 0; memcpy(mp,test,sizeof mp);//还原 } } int main(){ freopen("mayan.in", "r", stdin); freopen("mayan.out", "w", stdout); scanf("%d",&n); for(int i=0; i<5; i++){ for(int j=0; ; j++){ scanf("%d",&mp[i][j]); sum[mp[i][j]]++; if(mp[i][j] == 0) break; } } dfs(1); printf("-1\n"); return 0; }
考试总结
三道题,三个小时。这次比较满意的就是前两道题的效果,只用了一个小时,而且保证了正确度,诀窍就是成熟地分析问题,比较并论证各种方法,择优实行。(更重要的是题目不难【白眼】)不过很遗憾的是,尽管时间足够,第三道题还是没有拿到分。一个极大的原因就是搜索这种基础方法不能特别熟练应用。memcpy这种复制数组的方式其实是见过的,但是却没有想到。“访问无效内存”已经犯了多次了。怎么做到既不滥用数组,又不会出现这种错误呢?方法就是要清晰地理解程序的运行过程,精确范围。
相关文章推荐
- [NOIP2011] day1铺地毯,选择客栈,Mayan游戏
- NOIP 2011 题解 铺地毯 选择客栈 Mayan 游戏
- NOIP2011(DAY1)解题报告(C/C++)(铺地毯)(选择客栈)(Mayan 游戏)
- Noip 2011 解题报告 Day1 (铺地毯,选择客栈,Mayan 游戏)
- NOIP2011复赛提高组day1(A:铺地毯 B:选择客栈 C:mayan游戏)
- NOIP 2011 DAY 1 解题报告(铺地毯,选择客栈,mayan游戏)
- [NOIP2011提高组day1]-3-mayan游戏
- NOIP2011选择客栈 贪心
- NOIP2011 mayan游戏 解题报告(搜索)
- NOIP 2011 提高组 选择客栈(vijos 1737)(方法:队列,数学)
- 【NOIP2011】选择客栈
- 选择客栈 NOIP2011 提高组 Day1 T2
- NOIP2011 选择客栈
- COGS 621.[NOIP2011] 选择客栈 解题报告
- [NOIP2011]Mayan游戏 题解
- NOIP2011 选择客栈 题解(最简方法,超短代码)
- NOIP2011 选择客栈
- CodeVs 1135 && noip 2011 Day1-2 选择客栈
- [NOIP2011] 提高组 洛谷P1312 Mayan游戏
- NOIP 2011 选择客栈