您的位置:首页 > 其它

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这种复制数组的方式其实是见过的,但是却没有想到。“访问无效内存”已经犯了多次了。怎么做到既不滥用数组,又不会出现这种错误呢?方法就是要清晰地理解程序的运行过程,精确范围。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: