题目: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比字典树还要快啊,哈哈哈,我想这可能和字典树的链式结构相关。