NOIP 2011 DAY 1
第一题:铺地毯 //http://www.nyzoj.com:5283/problem/1111
题目
为了准备一个独特的颁奖典礼,组织者在会场的一片矩形区域(可看做是平面直角坐标系的第一象限)铺上一些矩形地毯。
一共有 n 张地毯,编号从 1 到 n。现在将这些地毯按照编号从小到大的顺序平行于坐标轴先后铺设,后铺的地毯覆盖在前面已经铺好的地毯之上。地毯铺设完成后,组织者想知道覆盖地面某个点的最上面的那张地毯的编号。
注意:在矩形地毯边界和四个顶点上的点也算被地毯覆盖。
输入
- 第一行,一个整数 n,表示总共有 n 张地毯。
- 接下来的 n 行中,第 i+1 行表示编号 i 的地毯的信息,包含四个正整数 a,b,g,k,每两个整数之间用一个空格隔开,分别表示铺设地毯的左下角的坐标 (a,b) 以及地毯在 x 轴和 y 轴方向的长度。
- 第 n+2行包含两个正整数 x 和 y,表示所求的地面的点的坐标 (x,y)。
输出
- 输出共 1 行,一个整数,表示所求的地毯的编号;若此处没有被地毯覆盖则输出 −1。
样例一
input
3
1 0 2 3
0 2 3 3
2 1 3 3
2 2
output
3
思路:
因为这道题是逐个覆盖地毯,所以我们可以模拟过程,如果这个地毯的范围覆盖了终点,那么就把答案跟新成这个地毯的序号。从前往后扫一遍,最后输出更新结果即可。
代码如下:
#include<cstdio> struct Carpet{ int x,y,g,k; }a[10005]; int n,tx,ty,ans; bool check(int x,int y,int lx,int ly) { if (tx<x || tx>x+lx) return false; if (ty<y || ty>y+ly) return false; return true; } int main() { scanf ("%d",&n); for (int i=1;i<=n;i++) scanf ("%d%d%d%d",&a[i].x,&a[i].y,&a[i].g,&a[i].k); scanf ("%d%d",&tx,&ty); for (int i=1;i<=n;i++) { if (check(a[i].x,a[i].y,a[i].g,a[i].k)) ans=i; } printf("%d",ans); return 0; }
第二题:选择客栈 //http://www.nyzoj.com:5283/problem/1112
题目
丽江河边有 n 家很有特色的客栈,客栈按照其位置顺序从 1 到 n 编号。每家客栈都按照某一种色调进行装饰(总共 k 种,用整数 0∼k−1 表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费。
两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过 p。
他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过 p 元的咖啡店小聚。
输入
- 第一行三个整数 n,k,p,每两个整数之间用一个空格隔开,分别表示客栈的个数,色调的数目和能接受的最低消费的最高值;
- 接下来的 n 行,第 i+1 行两个整数,之间用一个空格隔开,分别表示 i 号客栈的装饰色调和 i 号客栈的咖啡店的最低消费。
输出
- 输出只有一行,一个整数,表示可选的住宿方案的总数。
样例一
input
5 2 3
0 5
1 3
0 2
1 4
1 5
output
3
explanation
客栈编号 | ① | ② | ③ | ④ | ⑤ |
---|---|---|---|---|---|
色调 | 0 |
1 | 0 | 1 | 1 |
最低消费 | 5 |
3 |
2 | 4 | 5 |
2 人要住同样色调的客栈,所有可选的住宿方案包括:住客栈 ①③,②④,②⑤,④⑤,但是若选择住 ④、⑤ 号客栈的话,④、⑤ 号客栈之间的咖啡店的最低消费是 4,而两人能承受的最低消费是 3 元,所以不满足要求。因此只有前 3 种方案可选。
限制与约定
- 对于 30% 的数据,有 n≤100;
- 对于 50% 的数据,有 n≤1000;
- 对于 100% 的数据,有 2≤n≤20 0000,0<k≤50,0≤p≤100。
思路:
数据n范围较大,考虑尽量在O(n)范围内解决。对于每一个客栈,只考虑它前面的是否可以和它组成一种住宿方案,这样可以保证不重不漏,并且在O(n)范围内解决问题。从1~n号客栈依次输入并扫描:对于每一个当前客栈,有两种情况,1.当前客栈的最低费用小于p,
那么显然这个客栈可以作为喝咖啡的地点,记为pos。2.当前客栈的最低费用高于p,那么这个客栈和前面的客栈组成一种住宿方案只能是它们中间包括了一个可以喝咖啡的地方,即前面标记的pos,由此也可以看出,pos位置越靠当前客栈,之前扫描的同颜色的客栈就有更多,
也就意味可以构成更多的方案,所以pos是最靠近当前客栈的可以喝咖啡的地方。 对于当前的每一个客栈,用结构体存储该客栈之前颜色相同的客栈数量和上一个颜色相同客栈的位置(序号)。 last数组表示该颜色上一个客栈的位置。 cnt数组表示截至目前该颜色客栈的数量。
pos表示最靠近当前客栈的可以喝咖啡的客栈位置(序号)。 对于每一个当前客栈,找一个之前同颜色的客栈x,使得pos在它俩之间,并把答案加上x之前(包括x)所有同颜色客栈的数量即可。
代码如下:
#include<cstdio> struct Node{ int cnt,last; }hotel[200005]; int last[51],cnt[51]; int n,k,p,pos,tot; int main() { scanf ("%d%d%d",&n,&k,&p); for (int i=1;i<=n;i++) { int color,cost,t; scanf ("%d%d",&color,&cost); hotel[i].cnt=++cnt[color]; hotel[i].last=last[color]; last[color]=i; if (cost<=p) pos=i; t=hotel[i].last; while (pos<t) { t=hotel[t].last; } tot+=hotel[t].cnt; } printf("%d",tot); return 0; }
第三题:题目复杂这里不叙述了,是一道模拟+dfs,可以到这个网站看题目。 //http://www.nyzoj.com:5283/problem/1113
思路:题目中规定向右移优于向左移,所以每次考虑向右移,如果当前格是空格,就左移,标记一下即可。另外,如果有一种颜色的格子数=1或=2,那么一定不可能消去,返回并输出-1即可。
核心代码:
1.消除格子:
bool remove(int now[6][8]) { bool vis[6][8]={0},flag=0; for (int i=1;i<=5;i++) for (int j=1;j<=7;j++) if (now[i][j]) { if (i<=3 && now[i][j]==now[i+1][j] && now[i+1][j]==now[i+2][j]) vis[i][j]=vis[i+1][j]=vis[i+2][j]=1; if (j<=5 && now[i][j]==now[i][j+1] && now[i][j+1]==now[i][j+2]) vis[i][j]=vis[i][j+1]=vis[i][j+2]=1; } for (int i=1;i<=5;i++) for (int j=1;j<=7;j++) if (vis[i][j]) now[i][j]=0,flag=1; return flag; }
2.格子下落:
void falldown(int now[6][8]) { int k,tmp; for (int i=1;i<=5;i++) { k=0; for (int j=1;j<=7;j++) { tmp=now[i][j],now[i][j]=0; if (tmp) now[i][++k]=tmp; } } }
AC代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int lin[6][3],color[6][8],cnt[11],n; bool remove(int now[6][8]) { bool vis[6][8]={0},flag=0; for (int i=1;i<=5;i++) for (int j=1;j<=7;j++) if (now[i][j]) { if (i<=3 && now[i][j]==now[i+1][j] && now[i+1][j]==now[i+2][j]) vis[i][j]=vis[i+1][j]=vis[i+2][j]=1; if (j<=5 && now[i][j]==now[i][j+1] && now[i][j+1]==now[i][j+2]) vis[i][j]=vis[i][j+1]=vis[i][j+2]=1; } for (int i=1;i<=5;i++) for (int j=1;j<=7;j++) if (vis[i][j]) now[i][j]=0,flag=1; return flag; } void falldown(int now[6][8]) { int k,tmp; for (int i=1;i<=5;i++) { k=0; for (int j=1;j<=7;j++) { tmp=now[i][j],now[i][j]=0; if (tmp) now[i][++k]=tmp; } } } bool check(int now[6][8]) { for (int i=1;i<=5;i++) for (int j=1;j<=7;j++) if (now[i][j]) return 0; return 1; } void dfs(int step,int now[6][8]) { if (step>n) { if (check(now)) { for (int i=1;i<=n;i++) if (lin[i][2]) printf("%d %d -1\n",lin[i][0],lin[i][1]-1); else printf("%d %d 1\n",lin[i][0]-1,lin[i][1]-1); exit(0); } else return; } memset(cnt,0,sizeof(cnt)); for (int i=1;i<=5;i++) for (int j=1;j<=7;j++) ++cnt[now[i][j]]; for (int i=1;i<=10;i++) if (cnt[i]==1 || cnt[i]==2) return; int t[6][8]={0}; for (int i=1;i<5;i++) for (int j=1;j<=7;j++) if (now[i][j]!=now[i+1][j]) { memcpy(t,now,sizeof(t)); lin[step][0]=i,lin[step][1]=j,lin[step][2]=!t[i][j]; swap(t[i][j],t[i+1][j]); falldown(t); while (remove(t)) falldown(t); dfs(step+1,t); } return; } int main() { scanf ("%d",&n); for (int i=1;i<=5;i++) { for (int j=1;;j++) { int x; scanf ("%d",&x); if (x) color[i][j]=x; else break; } } dfs(1,color); printf("-1"); return 0; }
总结一下考试经验:第一题比较水,以后尽量压缩时间。第二题刚开始看起来有点懵,不过仔细想想每个节点情况单一,思维比较直观,要静下心认真分析。第三题模拟+dfs,挺恐怖的,思路一定要清晰,尽可能模拟写好。