hdu 1800 Flying to the Mars(字典树||map||hash)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1800

给出一堆士兵的等级,等级高的的士兵可以当等级小的士兵的师傅,一个士兵最多一个师傅(可以没有),一个师傅最多1个徒弟(可以没有),如果是师徒关系,可以用一把扫帚练习技能,问:全部士兵都用扫帚练习时需要的最少的扫帚数量?
这就是寻找一棵树的最大分支嘛,如果所有的数字均不相同那么必能连成一串,仅需一把扫帚,多一个重复的元素就要多一把扫帚,所以就是统计数字的最大重复次数。再次涉及到统计了。。能用字典树解决,同样也能用映射解决,当然map的思想来源于hash。
trie(运行时间:374MS):

#include <iostream>
#include<cstdio>
using namespace std;
int result;
typedef struct node{
    int num;
    bool fina;
    struct node *next[10];
}*trie,node;
trie root;
void init(trie &p){
    p=new node();
    for(int i=0;i<10;i++)p->next[i]=0;
    p->num=p->fina=0;
}
void insert(char s[]){
    trie p=root;
    int k=0;
    while(s[k]=='0')k++;
    while(s[k]){
        if(p->next[s[k]-'0']==0){
            trie q;
            init(q);
            p->next[s[k]-'0']=q;
        }
        p=p->next[s[k]-'0'];
        k++;
    }
    p->num++;
    p->fina=1;
    result=result>p->num?result:p->num;
}
int main()
{
    //freopen("cin.txt","r",stdin);
    int n;
    while(cin>>n){
        result=0;
        char s[35];
        init(root);
        for(int i=0;i<n;i++){
            scanf("%s",s);
            insert(s);
        }
        printf("%d\n",result);
    }
    return 0;
}
映射统计(运行时间:717MS):
#include <iostream>
#include<map>
#include<cstdio>
using namespace std;

int main()
{
    //freopen("cin.txt","r",stdin);
    int n,result;
    while(cin>>n){
        result=0;
        map<int,int> mp;
        for(int i=0;i<n;i++){
            int t;
            scanf("%d",&t);
            mp[t]++;
            result=result>mp[t]?result:mp[t];
        }
        printf("%d\n",result);
    }
    return 0;
}
最开始时自己惯性的用字符串和整数构成映射关系,结果,出现各种问题:
TLE:
#include <iostream>
#include<map>
#include<cstring>
#include<cstdio>
using namespace std;
struct cmp{
    bool operator()(char *s1,char *s2){
        return strcmp(s1,s2)<0;
    }
};
char s[3005][35];
int main()
{
    //freopen("cin.txt","r",stdin);
    int n,result;
    while(cin>>n){
        result=0;
        map<char *,int,cmp> mp;
        for(int i=0;i<n;i++){
            scanf("%s",s[i]);
            int k=0;
            while(s[i][k]=='0')k++;
            mp[s[i]+k]++;
            result=result>mp[s[i]+k]?result:mp[s[i]+k];
        }
        printf("%d\n",result);
    }
    return 0;
}
/
map<char *,int,cmp> mp;
        for(int i=0;i<n;i++){
            char s[35];     //只能输入一个字符串.
            scanf("%s",s);
            mp[s]++;
            result=result>mp[s]?result:mp[s];
        }

//
超时可能在于mp的内部排序,干脆不排序了,--> WA:
#include <iostream>
#include<map>
#include<cstring>
#include<cstdio>
using namespace std;
char s[3005][35];
int main()
{
    freopen("cin.txt","r",stdin);
    int n,result;
    while(cin>>n){
        result=0;
        //map<char *,int,cmp> mp;
        map<char *,int> mp; //不排序的映射导致即使重复出现,次数也记为1
        for(int i=0;i<n;i++){
            scanf("%s",s[i]);
            int k=0;
            while(s[i][k]=='0')k++;
            mp[s[i]+k]++;
            result=result>mp[s[i]+k]?result:mp[s[i]+k];
        }
        for(map<char *,int>::iterator ix=mp.begin();ix!=mp.end();ix++){
            cout<<ix->first<<" "<<ix->second<<endl;
        }
        printf("%d\n",result);
    }
    return 0;
}

同时也能看出,字典树在数据多和数据长度长后在时间复杂度上的优势。
hash的相关函数ELFhash是我开始学习时比较头疼的事儿,看了半天都没看懂。这是我看别人的讲解:
// ELF Hash Function
unsigned int ELFHash(char *str)
{
 unsigned int hash = 0;
 unsigned int x = 0;
 while (*str)
 {
 hash = (hash << 4) + (*str++);//hash左移4位,当前字符ASCII存入hash低四位。
 if ((x = hash & 0xF0000000L) != 0)
{//如果最高的四位不为0,则说明字符多余7个,如果不处理,再加第九个字符时,第一个字符会被移出,因此要有如下处理。
//该处理,如果对于字符串(a-z 或者A-Z)就会仅仅影响5-8位,否则会影响5-31位,因为C语言使用的算数移位
hash ^= (x >> 24);
//清空28-31位。
hash &= ~x;
}
}
//返回一个符号位为0的数,即丢弃最高位,以免函数外产生影响。(我们可以考虑,如果只有字符,符号位不可能为负)
return (hash & 0×7FFFFFFF);
}
多多实践理解会更加深入的。
hash(运行时间:124MS)

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=7000;
int hashx[maxn],countx[maxn],n,result;
int ELFhashx(char *key){
    unsigned long h=0,g;
    while(*key){ //一个字符需要用8位来表示,2^9-1=511. 2^4-1=15用四位只存储了字符的一半的信息。
        h=(h<<4)+*key++; //低四位存入字符
        g=h&0xf0000000L;  //g即是取h的二进制高4位,第8个字符的信息
        if(g) h^=g>>24;   //一个英文字符一个字节8位,英文字符ASCII:97--122.如果g是非0的,代表这是第8个字符
        h&=~g;
    }
    return h&0x7fffffff;
}
void hashxit(char *s){
    int k;
    while(*s=='0')  s++;
    k=ELFhashx(s);
    int t=k%maxn;
    while(hashx[t]!=k&&hashx[t]!=-1){
        t=(t+10)%maxn;
    }
    if(hashx[t]==-1){   countx[t]=1; hashx[t]=k;  }
    else if(++countx[t]>result)result=countx[t];
}
int main()
{
    //freopen("cin.txt","r",stdin);
    char str[100];
    while(cin>>n){
        memset(hashx,-1,sizeof(hashx));
        getchar();
        result=1;
        for(int i=0;i<n;i++){
            gets(str);
            hashxit(str);
        }
        cout<<result<<endl;
    }
    return 0;
}
没想到hash比字典树还要快啊,哈哈哈,我想这可能和字典树的链式结构相关。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值