POJ1002 487-3279

POJ上的一道题目:企业想拥有令人难忘的电话号码。要一个电话号码让人容易记住,方法之一是号码里面有一个难忘的单词或词组拼写。例如,您可以拨打TUT- GLOP拨打沃特卢大学。有时电话号码只有一部分是单词拼写。当你回到你的酒店,今晚你可以通过拨打310 - GINO吉诺订购比萨饼。另一种方法,设计的电话号码是一个难忘的方式分组数字。你可以通过拨打必胜客" 3个10 "的号码3-10-10-10 来订购比萨.

  一个电话号码的标准形式是7个十进制数字用连字符之间的第三和第四位数字(如888-1200 ).一个电话的小键盘提供字母数字的映射,如下所示:
A,B和C映射到2
D,E和F映射到3
G, H和I映射到4
J,K,和L映射到5
M,N和O映射到6
P,R和S映射到7
T,U,和V映射到8
W,X,和Y映射到9的

  没有包括字母Q和Z的映射,连字符不能拨号,可以根据需要添加和删除. TUT- GLOP的标准形式是888-4567, 310 - GINO的标准形式是310-4466 , 3-10-10-10的标准形式是310-1010 。两个电话号码是等价的,如果它们具有相同的标准形式.(他们拨打同一个号码。 )你的公司正在编制当地企业的电话号码目录。作为质量控制过程的一部分,你要检查,没有两个(或以上)的企业目录中的有相同的电话号码.
  输入输入将包括一个案例。输入的第一行中指定的目录中的电话号码的数目(最多10万)作为单独行上的一个正整数.其余各行列出在目录中的电话号码,在单独一行的每个数字。每个电话号码的十进制数字,大写字母(不包括Q和Z )和连字符组成的字符串组成. 整整7个数字或字母的字符串中的字符.

产量
  生成一行输出为每个电话号码,任何形式多次出现。该行应得到的标准形式的电话号码,后跟一个空格,其次是在电话号码出现在目录的次数。电话号码升字典顺序排列输出线。如果没有重复在输入打印线:

No duplicates.

采样输入
12
4873279
ITS- EASY
888-4567
3-10-10-10
888 - GLOP
TUT- GLOP
967-11-11
310 - GINO
F101010
888-1200
-4-8-7-3-2-7-9 - 
487-3279


样本输出
310-1010 2
487-3279 4
888-4567 3


  这道题目解法多种多样,难点在于时间复杂度的控制,花了好长时间在这道题目上,下面说说我的经验;
  刚开始我想定义一个数据结构来存储,str字符数组用来存储每一个电话号码(去掉了'-'字符);identy用于存储字符数组的第一个字符,用于排序;icount用来统计每个相同号码的个数,用一个for循环实现起来并不复杂,可是程序一直是TLE(Time Limit Exceeded),查阅了一些资料,不服气自己的程序就是不能通过这道题,一直在修改修改再修改,后来发现,我从一开始就错了,我错的是我把电话号码当成一个字符数组来处理,还创建了结构体来存储,假设有10万个电话号码,还要排序,那时间复杂度就很高了,而且在循环体里面使用了strcmp,strcpy等库函数,花在这上面的时间,灰常多,继续在这上面修改,也不会有多好的效果的,在论坛上得到了大神的启发,转战用数字才存放电话号码,int类型的足矣.

typedef struct
{
    int identy;
    char str[9];
    int icount;
}Phone;
于是,不采用结构体,直接用整型数组,这样不需要通过什么strcmp,strcpy函数来判断大小和赋值,也能直接用数组存储数据来排序,这样一来,时间复杂度将会大大减小。完整代码是:

#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100000
int cmp(const void *a, const void *b)
{
    int *x = (int *)a;
    int *y = (int *)b;
    return (*x) - (*y);
}
int main()
{
    int cnt = 0;   //存放电话号码个数
    int iter = 0;
    int Noduplicates = 0;  //有无重复号码的标志,为0则表示无重复
    int Sum = 0;
    int icount = 0;
    char ch;      // 接收键盘输入字符
    int *PHONE = (int *)malloc(sizeof(int) * (MAXSIZE + 10));
    scanf("%d",&cnt);
    getchar();
    
    //获取所有电话号码,转换为整型数据
    for(iter = 0; iter < cnt; iter++)
    {
        Sum = 0;
        ch = getchar();
        while(ch != '\n')
        {
            if(ch >= '0' && ch <= '9')
            {
                Sum = Sum * 10 + ch - '0' ;
            }
            else if(ch >= 'A' && ch <= 'P')
            {
                Sum = Sum * 10 + (ch - 65) / 3 + 2 ;
            }
            else if(ch >= 'R' && ch <= 'Y')
            {
                Sum = Sum * 10 + (ch - 66) / 3 + 2 ;
            }
            ch = getchar();
        }
        PHONE[iter] = Sum;
    }
    
    //用快排对电话号码排序
    qsort(PHONE, cnt, sizeof(int), cmp);
    
    //输出排好顺序的号码
    for(iter = 0, icount = 1; iter < cnt - 1 ; iter++)
    {
        icount = 1;
        //统计重复号码个数
        while(PHONE[iter] == PHONE[iter + 1])
        {
            iter++;
            icount++;
        }
        if(icount > 1)
        {
            Noduplicates = 1;
            printf("%03d-%04d %d\n",PHONE[iter] / 10000 ,PHONE[iter] % 10000 ,icount);
        }
    }
    if(Noduplicates == 0)
    {
        printf("No duplicates.\n");
    }
    getchar();
    return 0;
}
注: QZ 是不在映射里面的!还有就是输出格式,比如号码"000-0001"不应该输出"   -    1"(前导 0 的问题)

给几组测试用例:
 用例1:
2
---3333--3-3-3--
-3333333

输出:
333-3333 2

用例2:
4
0000000
0010001
0000000
0010001

输出:
000-0000 2
001-0001 2

要是这样你还不能AC,再看看下面几点建议:
1、数据量大时候果然要注意输入的时间,用cin超时,改用gets就AC了;
2、是不是原来是 "No duplicates." 少写了一个句号,或者后面多了一个空格输出格式有严格要求的;
3、如果自己写的快排不行,那可以试试系统的快排qsort(),我的程序就是用系统的快排;
4、用G++超时,但用C++AC了,-_-!!;
5、如果你是用字符数组才存储每个字符串(电话号码),字符串包括'-'字符,建议将数组长度开到40甚至100以上;
6、为了AC可以适当通过空间换取时间;
7、看到第7点建议,说明,我也不知道为什么了,你可以把原来的程序放一边,重新考虑其他算法,比如堆排序,hash算法,等通过了,再回头看看原来   的算法有什么不妥的地方,还是不行,请看第8点建议;

8、

注:本博客与博客园上的博客为同一博客主:http://www.cnblogs.com/bestDavid/

阅读更多
相关热词
换一批

有没有做ACM的?求救487-3279poj1002

04-14

[code=C/C++]rn#includern#includern#includern#includernrnstruct phonern int times;rn int visited;rn struct phone * next[10];rn;rnrnint chset[]= 2,2,2,rn 3,3,3,rn 4,4,4,rn 5,5,5,rn 6,6,6,rn 7,0,7,7,rn 8,8,8,rn 9,9,9,0;rnrnint han(char c)//转换为标准格式字符rn if(c>='0'&&c<='9')rn return c-48;rn if(c>='A'&&c<='Z')rn return chset[(c-65)];rn return -1;rnrnrnstruct phone * create()//初始化一个节点rn struct phone * t=(struct phone *)malloc(sizeof(struct phone));rn for(int i=0;i<10;i++)rn t->times=0;rn t->next[i]=0;rn t->visited=0;rn rn return t;rnrnrnstruct phone * head=create();//头结点rnrnchar s[7]="0";//存储标准格式号码rnint flag=0,times=0;rnrnvoid print(struct phone * p,int d)rn int i=0;rn p->times--;rn while((!p->next[i]))rn i++;rn rn if(d==7)rn if(p->visited)rn flag=1;rn return;rn rn p->visited=1;rn s[6]=i+48;rn times=p->times+1;//找到一个出现超过1次的号码,次数为timesrn return;rn rn if(p->next[i]->times<2)rn p->next[i]=0;rn flag=1;//此节点小于2次flag=1rn return;rn rn print(p->next[i],++d);rn if(flag)rn return;rn rn p->visited=1;rn s[d-2]=i+48;rn return;rnrnrnint main()rn struct phone *p = head, * tp;rn int n,i,j,t,NOans=1;rn char c;rn freopen("in.txt", "r", stdin);rn scanf("%d",&n);getchar();rn for(i=1;i<=n;i++)rn for(j=1;j<=7;j++)rn c=getchar();rn while(han(c)==-1)rn c=getchar();rn t=han(c);rn p->times+=1;rn if(p->next[t]==0)rn p->next[t]=create();rn rn p=p->next[t];rn rn p=head;rn rn while(p->times>1)rn print(p,1);rn if(!flag)rn for(int k=0;k<7;k++)rn if(k==3)rn printf("-");rn rn printf("%d",(int)s[k]-48);rn NOans=0;rn rnrn printf(" %d\n",times);rn memset(s,0,sizeof(s));rn times=0;rn rn flag=0;//重置flagrn rn if(NOans)rn printf("No duplicates.\n");rn rn return 0;rnrn[/code]rn希望大大们给出我这个算法的错误之处,别的方法我知道,我就只想知道这个算法是哪里不对了。

没有更多推荐了,返回首页