Noip 2011 解题报告 Day1 (铺地毯,选择客栈,Mayan 游戏)

来源:互联网 发布:网络彩票公司上班4个月 编辑:程序博客网 时间:2024/05/18 12:04

前言

在这春暖花开的日子,我们迎来了又一次的模拟测试。这一次,我们采用了2011年的Day1真题作为题目。
正题:

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。

代码:

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>using namespace std;const int N=10000+5;int n,get[N][5],ans=-1;int x,y;int main(){    freopen("carpet.in","r",stdin);    freopen("carpet.out","w",stdout);    scanf("%d",&n);    for(int i=1;i<=n;i++)    {        int a,b,g,k;        scanf("%d%d%d%d",&a,&b,&g,&k);        get[i][1]=a;        get[i][2]=b;        get[i][3]=a+g;        get[i][4]=b+k;    }    scanf("%d%d",&x,&y);    for(int i=1;i<=n;i++)    if(x>=get[i][1]&&x<=get[i][3]&&y>=get[i][2]&&y<=get[i][4]) ans=i;    printf("%d\n",ans);    return 0;}

非常的简单,稍微判断一下就可以了,注意条件有四个分别是if(x>=get[i][1]&&x<=get[i][3]&&y>=get[i][2]&&y<=get[i][4])
顺利满分。

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。

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>using namespace std;const int N=200000+5;int n,k,p,color[N],price[N],sum[N],pre[N],ans=0;//sum情况数 int get(int l,int r){//  return sum[r]-sum[l];    return sum[r]-sum[l-1];}int main(){    freopen("hotel.in","r",stdin);    freopen("hotel.out","w",stdout);    scanf("%d%d%d",&n,&k,&p);//n客栈数,k色调数,p最大消费值     for (int i=1;i<=n;i++)    scanf("%d%d",&color[i],&price[i]);//色调与消费     for (int j=0;j<k;j++)//对色调操作,一个一个试     {        memset(sum,0,sizeof(sum));        for (int i=1;i<=n;i++)        {            if (color[i]==j) sum[i]=sum[i-1]+1;//合并一个酒店             else sum[i]=sum[i-1];        }        memset(pre,0,sizeof(pre));        int last=0;        for (int i=1;i<=n;i++)        {            if (price[i]<=p) last=i;//满足的             pre[i]=last;        }//类似于合并的操作,pre表示这以前的可用号码         for (int i=1;i<=n;i++)        if (color[i]==j)        {            if (price[i]<=p) ans+=get(1,pre[i]-1);            else ans+=get(1,pre[i]);        }    }    printf("%d",ans);    return 0;}

这个程序有点慢,思想是看色调一致的酒店就合并,记录前面的方案数,最后用尾减去头就好了。满分。
听说还可以是动态规划或者线段树。。后来想了想动态规划,发现确实可以。
下面是动态规划的代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int Sum1=0,Sum2=0;int n,k,p;int mp[2000005],flag[2000005]={0};int cnt[60]={0};int main(){    freopen("hotel.in","r",stdin);    freopen("hotel.out","w",stdout);    scanf("%d%d%d",&n,&k,&p);    for(int i=1;i<=n;i++)    {        int a,b;        scanf("%d%d",&a,&b);        mp[i]=a;        if(b<=p) flag[i]=1;    }    for(int i=1;i<=n;i++)    {        if(flag[i])         {            memset(cnt,0,sizeof(cnt));            continue;        }        Sum1+=cnt[mp[i]];        cnt[mp[i]]++;    }    memset(cnt,0,sizeof(cnt));    for(int i=1;i<=n;i++)    {        Sum2+=cnt[mp[i]];        cnt[mp[i]]++;    }    printf("%d\n",Sum2-Sum1);    return 0;}

这就是动态规划的方法。

3.Mayan 游戏

(mayan.cpp/c/pas)
【问题描述】
Mayan puzzle 是最近流行起来的一个游戏。游戏界面是一个7 行5 列的棋盘,上面堆放着一些方块,方块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块之上。游戏通关是指在规定的步数内消除所有的方块,消除方块的规则如下:
1、每步移动可以且仅可以沿横向(即向左或向右)拖动某一方块一格:当拖动这一方块时,如果拖动后到达的位置(以下称目标位置)也有方块,那么这两个方块将交换位置(参见输入输出样例说明中的图6 到图7);如果目标位置上没有方块,那么被拖动的方块将从
原来的竖列中抽出,并从目标位置上掉落(直到不悬空,参见下面图1 和图2);
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 所示的局面。

世上最难的搜索题!
尽管知道这是搜索,但是并没有能力写出正解。。
下面是暴力程序,由于是顺序输入,并且保证了30%数据只在第一行,所以完全可以自己枚举。利用“脑搜”,加上“数据分析”,成功拿到了40分。
代码如下:

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>using namespace std;int n,map[6][8],line[6];int judge(){    for (int i=1;i<=5;i++)    for (int j=2;j<=7;j++)    {        if (map[i][j]!=-1) return 0;    } //  for (int i=1;i<=5;i++)//  if (map[i][1]==-1) return 0    return 1;}int work(){    int tot=0;    if (line[1]==1&&line[2]==-1&&line[3]==1&&line[4]==-1&&line[5]==1)    {        if (n>=2)         {            printf("0 0 1\n");            printf("4 0 -1");        }        else printf("-1");    }    if (line[1]==1&&line[2]==1&&line[3]==-1&&line[4]==-1&&line[5]==1)    {        if (n>=2)         {            printf("4 0 -1\n");            printf("3 0 -1");        }        else printf("-1");    }    if (line[1]==1&&line[2]==-1&&line[3]==-1&&line[4]==1&&line[5]==1)    {        if (n>=2)         {            printf("0 0 1\n");            printf("1 0 1");        }        else printf("-1");    }    if (line[1]==1&&line[2]==1&&line[3]==-1&&line[4]==1&&line[5]==-1)    {        if (n>=1)         {            printf("3 0 -1");        }        else printf("-1");    }    if (line[1]==-1&&line[2]==1&&line[3]==-1&&line[4]==1&&line[5]==1)    {        if (n>=1)         {            printf("1 0 1");        }        else printf("-1");    }    if (line[1]==1&&line[2]==-1&&line[3]==1&&line[4]==1&&line[5]==-1)    {        if (n>=1)         {            printf("0 0 1");        }        else printf("-1");    }    if (line[1]==-1&&line[2]==1&&line[3]==1&&line[4]==-1&&line[5]==1)    {        if (n>=1)         {            printf("4 0 -1");        }        else printf("-1");    }}int main(){    freopen("mayan.in","r",stdin);    freopen("mayan.out","w",stdout);    memset(map,-1,sizeof(map));    memset(line,-1,sizeof(line));    scanf("%d",&n);    for (int i=1;i<=5;i++)    {        for (int j=1;;j++)        {            int a;            scanf("%d",&a);            if (a==0) break;            else map[i][j]=a;        }    }//输入    if (judge()==1)     {        int maxn[5],maxx=0,now;        memset(maxn,0,sizeof(maxn));        for (int i=1;i<=5;i++)        line[i]=map[i][1];        for (int i=1;i<=5;i++)        maxn[line[i]]++;        for (int i=1;i<=5;i++)        if (maxn[i]>=maxx)         {            maxx=maxn[i];            now=i;        }        for (int i=1;i<=5;i++)        if (maxn[i]!=0&&maxn[i]%3!=0)        {            printf("-1");            return 0;        }         work();    }    else printf("-1");    return 0;}

然而正解异常的麻烦,下面我们看看大神的代码。

#include<iostream>#include<cstdio>#include<cctype>#include<cstring>#include<cstdlib>#include<fstream>#include<sstream>#include<algorithm>#include<map>#include<ctime>#include<set>#include<queue>#include<vector>#include<stack>using namespace std;typedef bool boolean;#define smin(a, b) a = min(a, b)#define smax(a, b) a = max(a, b)typedef class gameObject {    public:        int a[5][7];        int counter[5];        void down() {           //Fall down the blocks            for(int i = 0; i < 5; i++) {                counter[i] = 0;                for(int j = 0; j < 7; j++) {                    if(a[i][j]) {                        a[i][counter[i]] = a[i][j];                        if(counter[i] != j)                            a[i][j] = 0;                        counter[i]++;       //Count the number of each column                    }                }            }        }        boolean boom() {            boolean aFlag[5][7];            boolean status = false;            memset(aFlag, false, sizeof(aFlag));            for(int i = 0; i < 5; i++) {                for(int j = 0; j < 5; j++) {                    if(a[i][j] == a[i][j + 1] && a[i][j + 1] == a[i][j + 2] && a[i][j])                        aFlag[i][j] = aFlag[i][j + 1] = aFlag[i][j + 2] = true;                }            }            for(int i = 0; i < 3; i++) {                for(int j = 0; j < 7; j++) {                    if(a[i][j] == a[i + 1][j] && a[i + 1][j] == a[i + 2][j] && a[i][j])                        aFlag[i][j] = aFlag[i + 1][j] = aFlag[i + 2][j] = true;                }            }            for(int i = 0; i < 5; i++) {                for(int j = 0; j < 7; j++) {                    if(aFlag[i][j])                        a[i][j] = 0, status = true, counter[i]--;                }            }            return status;        }        boolean move(int x, int y, int d) {            if(x + d >= 5 || x + d < 0)     return false;   //Array index exceeded            if(d == -1 && a[x + d][y])  return false;       //Useless moving            if(a[x][y] == a[x + d][y])  return false;       //Both have the same color            swap(a[x + d][y], a[x][y]);            return true;        }        boolean finish() {            for(int i = 0; i < 5; i++)                if(counter[i])                    return false;            return true;        }}gameObject;ifstream fin("mayan.in");ofstream fout("mayan.out");int n;gameObject starter;inline void init() {    fin >> n;    for(int i = 0, x; i < 5; i++) {        while(true) {            fin >> x;            if(x == 0)  break;            starter.a[i][starter.counter[i]++] = x;        }    }}int bx[6], by[6], bd[6];void dfs(gameObject e, int t) {    if(t == n) {        if(e.finish()) {            for(int i = 0; i < t; i++) {                fout << bx[i] << " " << by[i] << " " << bd[i] << endl;            }            exit(0);        }        return;    }    for(int i = 0; i < 5; i++) {        for(int j = 0; j < e.counter[i]; j++) {            for(int k = 0; k < 2; k++) {                gameObject eu = e;                int d = (k == 0) ? (1) : (-1);                if(!eu.move(i, j, d))   continue;                eu.down();                while(eu.boom())                    eu.down();                bx[t] = i, by[t] = j, bd[t] = d;                dfs(eu, t + 1);            }        }    }}int main() {    init();    dfs(starter, 0);    fout << "-1";    return 0;}

非常的漂亮!

至此,本次模拟考试就算是圆满的总结。希望自己在接下来的日子里,可以越走越远!
2017 3 11

2 0