NOIP模拟赛(140,史上最惨的一次)

哈哈哈,140分,我也不想说什么了,直接看题吧。。。

第一题:还好,第一题做对了,不然就秀逗了。。。

第二题:
2.俄罗斯方块
(tetris.pas/c/cpp)
【问题描述】
Lvica是一位充满激情的计算机科学家。 他最近开始研究他的第一个电脑游戏:一个流行的俄罗斯方块的复制品。 尽管他还远未完成,但他关于游戏的设计支持将下图中显示的五种不同的俄罗斯方块图形放在一个矩阵中。 在将它放入俄罗斯方块的矩阵之前,图形可以旋转90度任意次数并着色。 此外,当前的游戏不支持将图形放置超出矩阵边界以外或与矩阵中的另一个现有图形重叠。
NOIP模拟赛(140,史上最惨的一次)_第1张图片
当Lvica在学校时,他的妹妹Marica开始玩这个游戏,她随机旋转,着色并放置图形,使相邻的图形颜色不同。如果两个图形拥有一个公共的边或者尖端的位置相触碰,则两个图形相邻。
当Lvica回来,他发现他的妹妹在游戏中放置的图形。他想知道俄罗斯方块矩阵中有多少种图形,他因为忙于改进游戏所以请你来帮助他解决这个问题。

【输入】
输入的第一行包含正整数N和M,代表俄罗斯方块矩阵的行和列的数量。
下面的N行包含代表矩阵的M个字符。每个字符都可以是“.”,表示空白,也可以是英文小写字母表示图形一部分。不同的字母代表不同的颜色,同一图形每部分颜色相同。

【输出】
输出5行。第i行必须包含上述游戏中给出的第i个图形的出现次数。

【输入输出样例】
4 5
aaaa.
.bb..
.bbxx
…xx
2
1
0
0
0

4 5
.aab.
aabb.
.cbaa
cccaa
1
0
1
1
1

5 7
.c…..
ccdddd.
caabbcc
aabbacc
…aaa.
1
1
2
1
1
【数据范围】
对于20%的数据:只有第一个图形会出现。
对于另外的20%的数据:只有第二个图形会出现。
对于另外的20%的数据:只有第三个图形会出现。
对于另外的20%的数据:只有第四个图形会出现。
对于100%的数据:1≤N,M≤10。

好吧这回只能复制粘贴了,毕竟在网上找不到这道变态的题目
先说思路,首先,我们要在每一个图形里面找一个“基准点”(反正我是以最上面的最左边的那个点作为基准点的)。之后,我们可以根据那个基准点的坐标为原点,写出剩余三个点的坐标。之后,我们还要考虑旋转的问题,经过严谨的计算发现,一个点,假设原来的坐标为(a,b),那么顺势针旋转90度时候的坐标就变成了(b,-a)。
接下来,就是寻找的过程了,我们可以先用深搜找出四个点的坐标,然后按照先上后下,先左后右的顺序排序,然后跟五种形状比对,看有没有匹配的,之后再将这四个点旋转,在比对有没有相符的点,知道找到为止。
大体思路就是这样,祭上代码~~~

#include
#include
#include
using namespace std;
const int d[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
char a[20][20];
int n,m,num[6],now[5][2];
bool visited[20][20];

int judge()
{
    int maxh=0,maxl=0,minh=20,minl=20;
    for(int i=0;i<=3;++i){
        maxh=max(maxh,now[i][0]);
        maxl=max(maxl,now[i][1]);
        minh=min(minh,now[i][0]);
        minl=min(minl,now[i][1]);
    }
    if(maxh-minh==1&&maxl-minl==1)
        return 1;
    if((maxh-minh==0&&maxl-minl==3)||(maxh-minh==3&&maxl-minl==0))
        return 2;
}

void dfs(int x,int y,int step,char target)
{
    if(step==4){
        num[judge()]++;
        return;
    }
    now[step][0]=x;
    now[step][1]=y;
    for(int i=0;i<4;++i){
        int nx=x+d[i][0];
        int ny=y+d[i][1];
        if(visited[nx][ny]==false&&a[nx][ny]==target&&nx>=1&&nx<=n&&ny>=1&&ny<=m){
            visited[nx][ny]=true;
            dfs(nx,ny,step+1,a[nx][ny]);
        }
    }
}

int main()
{
    freopen("tetris.in","r",stdin);
    freopen("tetris.out","w",stdout);
    cin>>n>>m;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            cin>>a[i][j];
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            if(visited[i][j]==false&&a[i][j]!='.'){
                dfs(i,j,0,a[i][j]);
            }
        }
    }
    for(int i=1;i<=5;++i)
        cout<return 0;
}

3.密码
(password.pas/c/cpp)
【问题描述】
最近,大型超人气社交网络 Secret Network的用户信息因为漏洞遭到了泄漏。这些机密信息是所有用户的密码。
一个叫Mihael的年轻学生最近在钻研信息安全。他发现整件事情非常有趣。在研究社交网络的时候,他发现了一个新的安全漏洞!输入密码时,当输入的字符串包含的一个子串与真正的用户密码相同时,就可以成功登录。举个例子,对于某位密码是abc的用户,只要输入abc,abcd 或者 imaabcnema 中的任意一个,系统就将成功登录,而输入 axbc 则会登录失败。
Mihael 想要知道存在多少个有序对使得该有序对中的两个用户不相同且使用有序对中第一个用户的密码可以登录有序对中第二位用户的账户。

【输入】
第一行输入包括一个正整数 N (1 <= N <= 20,000), N是用户的个数。
接下来的N 行每行输入包括该用户的密码。这些密码全部由英文字母表中的小写字母组成,其长度至少为1,不超过10。

【输出】
有且仅有一行输出,为任务中包含的有序对的数量。
【输入输出样例】
3
aaa
aa
abb ans:1

3
x
x
xy ans:4

5
mir
mirta
ta
ir
t ans:6

【样例解释】
对于第二个输入样例
用户1 和 用户2 为一个有序对
用户2 和 用户1 为一个有序对
用户3 和 用户1 为一个有序对
用户3 和 用户2 为一个有序对

【数据范围】
对于40 % 的测试数据,1 <= n <= 2000
对于100 % 的测试数据,1 <= n <= 20,000

好吧,这道题我没有做出来还情有可原,毕竟人家还没有学Hash表~
好了,言归正传,先来说一说这道题的做题思路,首按照先,如果按照常规做法的话,这道题的空间限制为64M,绝对会超时。。。
所以,我们要擎上我们的最终法宝:哈希!!!首先我们要将一个字符串转换成为一个27进制的数,这样好比较一些,但是我们发现,如果我们的数组开到27^10的话,空间绝对爆到让人窒息。我们发现这27^10的数组里面,只有55*20000=1100000有用,所以,我们可以先用程序找到一个不小于200000的质数(多留一些空间,是每一个数找位置的时候没有那么麻烦),然后每一个数再除以一下这个质数,得到的结果如果没有出现过,就把这个数放置在哪里,如果出现过,就考虑下一位,直到没有出现过,或者这一位的数相同的时候才把这个数加进这个神奇的数组里面,并且还要区分这是一个字符串的字串,还是自己本身就是一个字符串。
首先,我们先将每一个字符串的字串(包括自己)求出来,再存入那个神奇的数组里面,之后的事情就很简单了,答案等于所有数组里面的数的字串*字符串数量之和~
如果还是没有看懂的话,那就直接看我的代码把~~~

#include
#include
#include
#include
using namespace std;
const int maxN=20010;
const int Prime=2000003;
string s;
struct node{
    int a,b,c;
    long long zhi;
}hash[Prime];
int n;


void handle(int x,long long value,int p)
{
    int po=value%Prime;
    while(hash[po].zhi>0&&hash[po].zhi!=value)
        po=(po+1)%Prime;
    if(hash[po].a==x)
        return;
    hash[po].zhi=value;
    hash[po].a=x;
    if(p==2)
        hash[po].b++; 
    else
        hash[po].c++;
}

void work(int x)
{
    int len=s.size();
    for(int i=0;i<=len-1;i++){
        long long value=0;
        for(int j=i;j<=len-1;j++){
            value=value*27+s[j]-'a'+1;
            if(i==0&&j==len-1)
                handle(x,value,3);
            else
                handle(x,value,2);
        }
    }
}

void init()
{
    cin>>n;
    for(int i=1;i<=n;++i){
        cin>>s;
        work(i);
    }
}

int main()
{
    freopen("password.in","r",stdin);
    freopen("password.out","w",stdout);
    init();
    int ans=0; 
    for(int i=0;i<=Prime-1;++i)
        ans+=hash[i].b*hash[i].c+hash[i].c*(hash[i].c-1);
    cout<return 0;
}

终于写到了第四题了:
4.秘密通道
(portal.pas/c/cpp)
【问题描述】
有一副n*m的地图,有n*m块地,每块是下列四种中的一种:
墙:用#表示,墙有4个面,分别是前面,后面,左面,右面。
起点:用C表示,为主角的起点,是一片空地。
终点:用F表示,为主角的目的地,是一片空地。
空地:用 . 表示。
其中除了墙不能穿过,其他地方都能走。

主角有以下3种操作:
1.移动到相邻的前后左右的地方,花费一个单位时间。
2.向前后左右其中一个方向发射子弹,子弹沿直线穿过,打在最近的一堵墙的一面,然后墙的这面就会形成一个开口通往秘密通道。同一时间最多只能有两个开口,若出现有3个开口,出现时间最早的开口会立即消失。该操作不用时间。
3.可以从一个与开口相邻的空地跳进去,进入秘密通道,从另外一个开口正对的空地跳出来。这个过程花费一个单位时间。

地图四周都是墙,问主角最少用多少时间从C走到F。C和F只会出现一次。
【输入】
第一行输入两个正整数n,m。
接下来n行,每行m个字符描述地图。
【输出】
输出1个整数,表示最短时间完成路途。如果无解输出nemoguce
【输入输出样例】
NOIP模拟赛(140,史上最惨的一次)_第2张图片

【样例2解释】
总共用到8次操作,时间之和为4。如下图所示
1.向左射一枪,在(3,1)的右面出现开口。
2.向下射一枪,在(6,2)的上面出现开口。
3.向左从(3,1)进入秘密通道,从(6,2)中出来,到达(5,2)。用1单位时间。
4.向右射一枪,在(5,7)的左面出现开口,(3,1)右面的开口消失。
5.走进(6,2)的开口,出来到(5,6)。用1单位时间。
6.向上射一枪,在(1,6)的下面出现开口。
7.经过秘密通道,走到(2,6)。用1单位时间。
8.走到终点。用1单位时间。

这道题,是一个人都会往搜索那边靠(无论是宽搜还是深搜)。
但 是 这道题的正解并不是你们所想的这么简单,我们的目标是星辰大海~咳咳,言归正传。这道题的本质是用最短路(虽然我不知道出题人是怎么想到这种做法的),首先,这种题有两种连边,第一种就是每一个格子向他周围四个方 向连一条长度为 1 的边。第二种就是每一个格子向他上下左右的第一堵墙连一 条长度为格 子与最近的墙距离的边。 从 C 到 F 求一遍最短路即可。 假如不存在路径,就输出 nemoguce
最后,暴力上代码~

#include
#include
#include
#include
#include
using namespace std;
const int maxN=510;
struct bian
{
    int poh,pol,next,value;
}edge[maxN*maxN*10];
int tot,head[maxN][maxN];
int n,m,sth,stl,enh,enl;
int up[maxN][maxN],down[maxN][maxN],rightt[maxN][maxN],leftt[maxN][maxN];
int a[maxN][maxN];
int b[maxN*maxN][2],dis[maxN][maxN],st,en,num;
bool book[maxN][maxN];

void prepare()
{
    for(int i=1;i<=m;++i){
        up[1][i]=1,down[n][i]=n;
        for(int j=2;j<=n;++j){
            if(a[j][i]==0)
                up[j][i]=j;
            else
                up[j][i]=up[j-1][i];
        }
        for(int j=n-1;j>=1;--j){
            if(a[j][i]==0)
                down[j][i]=j;
            else
                down[j][i]=down[j+1][i];
        }
    }
    for(int i=1;i<=n;++i){
        leftt[i][1]=1,rightt[i][m]=m;
        for(int j=2;j<=m;++j){
            if(a[i][j]==0)
                leftt[i][j]=j;
            else
                leftt[i][j]=leftt[i][j-1];
        }
        for(int j=m-1;j>=1;--j){
            if(a[i][j]==0)
                rightt[i][j]=j;
            else
                rightt[i][j]=rightt[i][j+1];
        }
    }
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            dis[i][j]=-1;
        }
    }
    dis[sth][stl]=0;
}

void insert(int na,int nb,int x,int y,int value)
{
    edge[++tot].poh=x;
    edge[tot].pol=y;
    edge[tot].next=head[na][nb];
    edge[tot].value=value;
    head[na][nb]=tot;
}

void init()
{
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i][j]==1){
                if(a[i+1][j]==1) insert(i,j,i+1,j,1);
                if(a[i-1][j]==1) insert(i,j,i-1,j,1);
                if(a[i][j+1]==1) insert(i,j,i,j+1,1);
                if(a[i][j-1]==1) insert(i,j,i,j-1,1);
            }
        }
    }
}

void init2()
{
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i][j]==1){
                int len=min(i-up[i][j],min(down[i][j]-i,min(j-leftt[i][j],rightt[i][j]-j)));
                insert(i,j,up[i][j]+1,j,len);
                insert(i,j,down[i][j]-1,j,len);
                insert(i,j,i,leftt[i][j]+1,len);
                insert(i,j,i,rightt[i][j]-1,len);
            }
        }
    }
}

void spfa()
{
    st=en=1;
    b[1][0]=sth;
    b[1][1]=stl;
    num=n*m;
    book[sth][stl]=true;
    while(num>0){
        int t=head[b[st][0]][b[st][1]];
        while(t!=0){
            int y1=edge[t].poh;
            int y2=edge[t].pol;
            if(dis[y1][y2]>dis[b[st][0]][b[st][1]]+edge[t].value||(dis[y1][y2]==-1)){
                dis[y1][y2]=dis[b[st][0]][b[st][1]]+edge[t].value;
                if(book[y1][y2]==false){
                    en=(en+1)%(n*m);
                    b[en][0]=y1;
                    b[en][1]=y2;
                    book[y1][y2]=true;
                    num++;
                }
            }
            t=edge[t].next;
        }
        book[b[st][0]][b[st][1]]=false;
        st=(st+1)%(n*m);
        num--;
    }
}

int main()
{
    freopen("portal.in","r",stdin);
    freopen("portal.out","w",stdout);
    cin>>n>>m;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            char x;
            cin>>x;
            if(x=='C'){
                sth=i;
                stl=j;
                a[i][j]=1;
            }
            if(x=='F'){
                enh=i;
                enl=j;
                a[i][j]=1;
            }
            if(x=='.')
                a[i][j]=1;
        }
    }
    prepare();
    init();
    init2();
    spfa();
    if(dis[enh][enl]==-1)
        cout<<"nemoguce"<else
        cout<return 0;
}

啊,终于写完了,老师光是讲就讲了一天半,说实话,这些题系真的恶心。。。

你可能感兴趣的:(C++,NOIP,Junior)