ACM分享第一周

来源:互联网 发布:cf卡数据恢复多少钱 编辑:程序博客网 时间:2024/04/30 08:34

第一题:分拆素数和

Problem Description

把一个偶数拆成两个不同素数的和,有几种拆法呢?

Input

输入包含一些正的偶数,其值不会超过10000,个数不会超过500,若遇0,则结束。

Output

对应每个偶数,输出其拆成不同素数的个数,每个结果占一行。

Sample Input

30260

Sample Output

32

解题思路:

一开始的思路是这样的,结果出现 Time Limit Exceeded :    1.定义一个判断是否n为素数的调用函数 int IsPrime(int n) ,方便在主函数中进行调用;    2.定义int型count,用于对拆法进行计数;    3.for(int i = 2; i < n / 2; i++),判断拆分的第一个数i是否为素数,若为素数,则判断n-i是否为素数,如果也是素数,则sum++。解决办法:在 int IsPrime(int n) 函数中,将判断条件 i<n 改为 i<=sqrt(n) 即可。

我的代码:

#include <iostream>#include <math.h>using namespace std;int IsPrime(int n)  //判断是否为素数{    int i;    for(i = 2; i <= sqrt(n); i++)   //将n改为sqrt(n)后,超时问题得到解决    {        if(n % i == 0)        return 0;   //表明n不是素数    }    return 1;   //表明n是素数}int main(){    int n,sum;  //n为要键入的正偶数,sum为拆法总数    cin>>n;    while(n)    {        sum = 0;    //对于每一个键入值,初始拆法总数为0        for(int i = 2; i < n / 2; i++)  //不能等于,因为是两个不同的素数        {            if(IsPrime(i)&&IsPrime(n-i))  //如果拆分的两个数为素数                sum++;        }        cout<<sum<<endl;        cin>>n;    }    return 0;}

第二题:find your present(2)

Problem Description

In the new year party, everybody will get a "special present". Now it's your turn to get your special present, a lot of presents now putting on the desk, and only one of them will be yours. Each present has a card nember on it, and your present's card number will be the one that different from all the others, and you can assume that only one number appear odd times. For example, there are 5 present, and their card numbers are 1,2,3,2,1. so your present will be the one with the card number of 3, because 3 is the number that different from all the others.

Input

The input file will consist of several cases.Each case will be presented by an integer n(1<=n<1000000,and n is odd) at first. Following that, n positive integers will be given in a line, all integers will smaller than 2^32. These numbers indicate the card numbers of the presents. n = 0 ends the input.

Output

for each case, output an integer in a line, which is the card number of your present.

Sample Input

51 1 3 2 231 2 10

Sample Output

32

Hint

use scanf to avoid Time Limit Exceeded

解题思路:

 我理解的题意是这样的:    a.程序包括几个测试实例,每个测试实例第一行输入一个整数n(1<=n<1000000,且n为奇数),当n=0时,结束输入;    b.每个测试实例的第二行为n个正整数,所有整数小于2^31;    c.对于每个测试实例,输出第二行中唯一出现奇数次的整数,即为你的礼物卡片数值。  我一开始的思路是下面所述,结果出现 Runtime Error(ACCESS_VIOLATION):    1.设置两个数组 a[1000000+5] 和 b[1000000+5],分别用于存放输入的n个整数,和 a[i]出现的次数 b[a[i]]。     2.从第一个数开始,计算它出现的个数,若为奇数,则退出循环,该数即为所求,否则,继续从下一个 b[a[i]]==0 的a[i]值循环。 原因:    卡片值为小于2^31的正整数,当a[i]略小于2^31时,b[a[i]]将会越界。 为此,我去掉了b[1000010]数组,改用sum代替,并新增 判断卡片值是否曾出现过的函数 int IsAppear(int i),然而 Time Limit Exceeded了。 另一种思路是采用 异或(二进制) 的方法,开始设int型temp的值为0,将temp逐一与键入的n个卡片值进行异或,最后的temp值即为所求。

我的代码:

//我一开始的代码是这样的#include <stdio.h>long long int a[1000010],b[1000010],i,j,n;  //分别存放输入的n个整数,和a[i]出现的个数b[a[i]]//由于1000010过大,必须放在main函数外作全局变量,否则堆栈溢出,程序异常终止int main(){    while(scanf("%lld",&n) != EOF && n != 0)    {        for(i = 0; i < n; i++)        {            scanf("%lld",&a[i]);            b[a[i]] = 0;    出现次数先初始为0        }        for(i = 0; i < n; i++)        {            if(b[a[i]]==0)                b[a[i]]++;            else                continue;   //表明该a[i]值曾出现过并计过数            for(j = i+1; j < n; j++)            {                if(a[j] == a[i])                b[a[i]]++;            }            if(b[a[i]] % 2 != 0)//a[i]出现奇数次            {                printf("%I64d\n",a[i]);                break;            }        }    }    return 0;}//修改后超时代码:#include <stdio.h>long long int a[1000010],i,j,n,sum;  //分别存放输入的n个整数int IsAppear(int i) //用于判断a[i]是否曾出现过{    if(i>0)    {        for(int k = 0; k < i; k++)        {            if(a[k]==a[i])                return 0;        }    }    return 1;}int main(){    while(scanf("%lld",&n) != EOF && n != 0)    {        for(i = 0; i < n; i++)        {            scanf("%lld",&a[i]);        }        sum=0;        for(i = 0; i < n; i++)        {            if(IsAppear(i))                sum++;            else                continue;   //表明该a[i]值曾出现过并计过数            for(j = i+1; j < n; j++)            {                if(a[j] == a[i])                    sum++;            }            if(sum % 2 != 0)    //a[i]出现奇数次            {                printf("%lld\n",a[i]);                break;            }        }    }    return 0;}

//采用 异或方法 的代码#include <stdio.h>int main(){    int i,n,temp,a;    while(scanf("%d",&n)!=EOF && n != 0)    //n为卡片总数    {        temp=0;        while(n--)        {            scanf("%d",&a);     //输入的卡片值,a为正整数            temp = a ^ temp;    //异或,先转换成二进制数再进行异或        }        printf("%d\n",temp);    }    return 0;}

第三题:A计划

Problem Description

可怜的公主在一次次被魔王掳走一次次被骑士们救回来之后,而今,不幸的她再一次面临生命的考验。魔王已经发出消息说将在T时刻吃掉公主,因为他听信谣言说吃公主的肉也能长生不老。年迈的国王正是心急如焚,告招天下勇士来拯救公主。不过公主早已习以为常,她深信智勇的骑士LJ肯定能够将她救出。现据密探所报,公主被关在一个两层的迷宫里,迷宫的入口是S(0,0,0),公主的位置用P表示,时空传输机用#表示,墙用*表示,平地用.表示。骑士们一进入时空传输机就会被转到另一层的相对位置,但如果被转到的位置是墙的话,那骑士们就会被撞死。骑士们在一层中只能前后左右移动,每移动一格花1时刻。层间的移动只能通过时空传输机,且不需要任何时间。

Input

输入的第一行C表示共有C个测试数据,每个测试数据的前一行有三个整数N,M,T。N,M迷宫的大小N*M(1<=N,M<=10)。T如上所意。接下去的前N*M表示迷宫的第一层的的布置情况,后N*M表示迷宫第二层的布置情况。

Output

如果骑士们能够在T时刻能找到公主就输出“YES”,否则输出“NO”。

Sample Input

 1   5 5 14 S*#*.  .#...  .....   ****. ...#. ..*.P #.*.. ***.. ...*. *.#..

Sample Output

YES

解题思路:

 1.对数据进行预处理,简化后面搜索时的判断:    a.上下两层都是‘#’是无意义的,可都标记为‘*’;    b.上下两层出现一个‘#’和一个‘*’也是没有必要考虑的,可都标记为‘*’。 2.运用广度优先搜索,由于题目涉及多层转换,可用三维数组解决,恰好层数可用0,1表示,两个层次之间的转换只要取反即可。 3.搜索思路:    a.出队列(第一个元素默认在队列中),遇到‘.’或者‘S’对其向四个方向扩展,只要不是‘*’就入队,然后将当前的节点标记为已经访问。    b.遇到‘#’则先判断它的对应的另一层的节点是否已经访问过,如果没有将其入队,否则忽略。    c.将刚出队列的元素标记为已经访问过。    d.如果能在规定T时间内找到公主,bfs返回正值,否则返回0。

我的代码:

#include<stdio.h>#include<string.h>#define MAX 15  //定义的是迷宫的大小typedef struct node{    int x , y ; //x,y的意思是当前位置的横纵坐标    int s , c ; //s为目前为止的用时,c指的的是当前的层数}Q;Q q[MAX*MAX*3] ;//队列中元素数组bool vi[2][MAX][MAX] ; //定义当前坐标是否被访问过char m[2][MAX][MAX] ;  //定义三维坐标int d[4][2] = {{0,1},{0,-1},{1,0},{-1,0}} ;//从某点扩展到其它四个方向int N,M,T;int ok(int i,int j) //边界函数,判断是否出界{    if(i>=0&&i<N && j>=0&&j<M)        return 1 ;    return 0 ;}void remake()   //这个函数对数据进行预处理{    for(int i = 0 ; i < N ; i++)        for(int j = 0 ; j < M ; j++)        {            char &a = m[0][i][j] , &b = m[1][i][j] ;//引用调用,简化代码            if(a=='#' && b=='#')//上下两层都为传送门,无意义,都记为‘*’                a = b = '*' ;            else if((a=='#' && b=='*')||(a=='*' && b=='#')) //一层为传送门,另一层为墙,也无意义                a = b = '*' ;        }}void init() //读取初始化数据{    scanf("%d%d%d",&N,&M,&T) ;  //输入迷宫大小,T为所需时刻    for(int c = 0; c < 2 ; c++) //c表示层数    {        for(int i = 0 ; i < N ; i++)        scanf("%s",m[c][i]) ;   //以字符串形式键入每层每行    }}void inq(int x , int y ,int head ,int &tail ,int o) //入队的函数,o用来判断是否需要层数取反{   //x,y为当前要入队坐标,head为当前对列头指针    int c , s ;    c = o ? !(q[head].c) : q[head].c ; //当前层数    s = o? q[head].s : q[head].s+1 ;   //目前为止用时,如果层数取反,时间无需加1,否则在同一层移动,时间加1    q[tail].x = x ;    q[tail].y = y ;    q[tail].c = c ; //更新对列入队元素    q[tail++].s = s ;//入队,尾指针加1,当前尾指针指向最后一位元素后一位置}int bfs()   //广度优先搜索{    int head=0 , tail=1 ,x , y ,c ; //对列中已入一个元素S    while(head < tail) //表示队列中有元素不为空    {        x = q[head].x , y = q[head].y ,c = q[head].c ;        char &tmp = m[c][x][y] ;//引用,取得当前坐标        if(tmp == 'P' && q[head].s <= T)//达到可行解            return q[head].s ;//返回目前用时        if(tmp == '#')  //时空机换层        {            if(!vi[!c][x][y])//如果另一层是没有访问过的                inq(x,y,head,tail,1) ;//入队,层数取反            head++ ; //出队列            vi[c][x][y] = 1 ;//标记出队元素            continue ;//结束当前循环        }        for(int i = 0 ; i < 4 ; i++)//如果是“#”以外的元素向四周扩展        {            int k = x+d[i][0] , v = y + d[i][1] ;            if(ok(k,v) && !vi[c][k][v] && m[c][k][v] != '*')//在边界内,没有访问过,而且不是墙                inq(k,v,head,tail,0) ;        }        vi[c][x][y] = 1 ;//将出队元素标记访问        head++ ;//出队,头指针加1    }//while    return 0 ; //没有找到可行解返回0}int main(){    int ca ;    scanf("%d",&ca) ;    while(ca--)    {        memset(vi,0,sizeof(vi)) ;//重置访问标记数组        init() ;//读取数据        remake() ;//预处理数据        if(bfs())            printf("YES\n") ;//如果找到        else             printf("NO\n") ;    }    return 0 ;}
0 0