您的位置:首页 > 其它

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()要快得多;

成长, 历练, 所向披靡

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: