找一组数据在另一组数据中是否出现过时,可以采用此法
也就是上面提到的用字符串对应下标
若只有大写字母,则采用26进制,若加上了小写字母,则采用52进制,若再加上了数字,可以采用62进制,但假如数字再最后,还可以直接采用52进制转换完成后,把数字跟上即可,然后小格格里面放上次数即可(别忘了先初始化,要不放不进去)。
补充: R进制转十,进制,假设最高项系数是a1之后一直到n,则从1开始循环n-1(因为最高项是R的n-1次幂)次,循环语句为: y=y*R+a;百试百灵,密不外传,哈哈。
#include
#include
#include
#include
#include
using namespace std;
#define M 300000//姓名哈希过去的上界
vector<int> an[M];//每个学生选择的课程编号
int getID(char* name){
//用其来找每个人的ID号
int y=0;
for(int i=0;i<3;i++){
//先把前三个字母转化过去,
y=y*26+(name[i]-'A');
}
y=y*10+(name[3]-'0');
return y;
}
char name[10];
int n,k,a,b,c,d;//n是人数,k是课程数,a是课程号,b是选课人数,c是名字对应过来的数,d用来放该学生选课数
int main(){
while(scanf("%d%d",&n,&k)!=EOF){
for(int i=0;i<k;i++){
scanf("%d%d",&a,&b);
for(int j=0;j<b;j++){
scanf("%s",name);
c=getID(name);
an[c].push_back(a);
}
}
for(int i=0;i<n;i++){
scanf("%s",name);
c=getID(name);
printf("%s %d",name,an[c].size());
sort(an[c].begin(),an[c].end());
for(int j=0;j<an[c].size();j++){
printf(" %d",an[c][j]);
}
printf("\n");
}
}
return 0;
}
#include
#include
#include
#include
using namespace std;
vector<int> course_stu[3000];
char name[40010][5];
int cmp(int a,int b){
return strcmp(name[a],name[b])<0;//按字典顺序排列
}
int main(){
int n,k;
scanf("%d%d",&n,&k);//n是学生数,k是课程数
for(int i=0;i<n;i++){
//输入的时候是先输入的学生,内层才是课程,只是存储的时候需要倒过来
int ncrs,crs;//ncrs表示所选课程总数,crs表示所选课程的编号
scanf("%s%d",name[i],&ncrs);
for(int j=0;j<ncrs;j++){
scanf("%d",&crs);
course_stu[crs].push_back(i);
}
}
for(int i=1;i<=k;i++){
printf("%d %d\n",i,course_stu[i].size());
sort(course_stu[i].begin(),course_stu[i].end(),cmp);
for(int j=0;j<course_stu[i].size();j++){
printf("%s\n",name[course_stu[i][j]]);
}
}
return 0;
}
A1063
题意:算重复率,分子是交集,分母是并集
思路:这个题的思路就是求一个交集,一个并集,首先用set存每个集合,然后,交集的话,可以用哈希表来做,并集的话,也可以用哈希表来做,但是哈希表对应是个问题,10^9太大了,不能直接对应,但是,小了估计也不行,所以这里要另谋出路,因为set有一个函数可以进行查找值,所以问题解决了。
补充: printf的输出格式
%5.2f,两位小数,前面5位
%5d,前面五位的整数
%-d,左对齐,否则为右对齐
#include
#include
#include
using namespace std;
set<int> st[60];
int main(){
int n,mem,ele;//n表示的是有几个集合,mem表示里面有几个元素,ele表示的是集合的元素
scanf("%d",&n);
for(int i=1;i<=n;i++){
//此处的i表示第几个集合
scanf("%d",&mem);
for(int j=0;j<mem;j++){
scanf("%d",&ele);
st[i].insert(ele);
}
}
int que,j,b,q,u;//que表示几组查询,j表示交集元素个数,b表示并集元素个数,q和u表示比较的两组
scanf("%d",&que);
for(int i=0;i<que;i++){
//i负责查询循环
scanf("%d%d",&q,&u);
j=0;b=st[q].size();
for(set<int>::iterator it=st[u].begin();it!=st[u].end();it++){
//开始比较了,以u找q
if(st[q].find(*it)!=st[q].end()){
//不在最后出现,说明找到了
j++;
}
else b++;//没找到就只加并集
}
printf("%.1f%\n",j*100.0/b);
}
return 0;
}
A1060
题意:科学记数法的转换
思路:数有两种,0.几几几的那种,和几几几.几几几的那种,有一个小陷阱,就是可能有前导0,要先去掉,然后,剩下的,就是找指数e和前面几个数字了,指数的e:0.几几几的这种,从.开始往后数,一直到数字,几几几的这种就是从头数到点。 前面的数字:0.几几几的这种先去掉前面的0,然后取数,几几几点的这种,先去掉小数点,然后取数。观察发现,其都跟小数点有关,也就是说,要么都得到小数点,要么都从小数点开始,所以先去掉小数点,去小数点的过程中数指数。然后这个都是对于一串字符的处理所以用string。
注意:
‘’
#include
#include
#include
#include
using namespace std;
void DealS(string &str,int dig,int &e,string &num){
//去掉前导0,去除小数点,找e,找num
while(str.length()>0&&str[0]=='0'){
//去掉前导0
str.erase(str.begin());
}
if(str.length()==0)e=0;
else if(*(str.begin())=='.'){
//表示0.几几几的类型
str.erase(str.begin());//先去掉小数点
while(str.length()>0&&str[0]=='0'){
e--;
str.erase(str.begin());
}
if(str.length()==0)e=0;
}
else{
//表示几几几.几几几的类型
int k;
for(k=0;k<str.length()&&str[k]!='.';k++){
e++;
}
if(k<str.length()){
str.erase(str.begin()+str.find('.'));
}
}
while(str.length()<dig){
str+='0';
}
num=str.substr(0,dig);
}
int main(){
string str1,str2;
int dig,e1,e2;//dig表示有效数字的位数,e表示指数,num表示取到的数字
e1=e2=0;
string num1,num2;//num表示取到的数字
while(scanf("%d",&dig)!=EOF){
cin>>str1>>str2;
DealS(str1,dig,e1,num1);
DealS(str2,dig,e2,num2);
if(e1==e2&&num1==num2){
//判断相等并输出
cout<<"YES "<<"0."<<num1<<"*10^"<<e1<<endl;
}
else{
cout<<"NO "<<"0."<<num1<<"*10^"<<e1<<" 0."<<num2<<"*10^"<<e2<<endl;
}
}
return 0;
}
A1100
题意:给了火星文和地球文的转换方法,给一个数并进行转化
思路:因为数不是很多,所以直接把所有的都整出来再输出就行了,但是必须是双向的,也就是说,输入字符串得对应数字,输入数字得对应字符串,但是,如果这里很多的话,就用哈希函数了,但是很少数据,所以直接用map解决字符串对应数字的问题即可。然后就是数制转换的问题了,数制转换,正常的转化方式是R进制高位开始乘R,加起来,但是这种方法不是很好因为有一些数总是重复计算的,比如单个位和单十位总是重复计算,所以把他单拿出来先算就好了,然后组合一下就可以。
注意:
#include
#include
#include
#include
#include
#include
using namespace std;
map<string,int> trans;
string res[13*13];
void creat(string g[20],string s[20]){
//个位用g,十位用s
trans["tret"]=0;res[0]="tret";
for(int i=0;i<12;i++){
//先算所有单个位的
trans[g[i]]=i+1;
res[i+1]=g[i];
}
for(int i=0;i<12;i++){
//再算十位的
trans[s[i]]=13*(i+1);
res[13*(i+1)]=s[i];
}
for(int i=0;i<12;i++){
//再算剩下的,就是两位都有的了
for(int j=0;j<12;j++){
string str=s[i]+' '+g[j];
trans[str]=trans[s[i]]+trans[g[j]];
res[trans[s[i]]+trans[g[j]]]=str;
}
}
}
int recog(string str){
if(str[0]>='0'&&str[0]<='9')return 1;
if(str[0]>='a'&&str[0]<='z')return 2;
}
int main(){
string g[20]={
"jan", "feb","mar", "apr", "may", "jun", "jly", "aug", "sep", "oct", "nov", "dec"};
string s[20]={
"tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", "syy", "lok", "mer", "jou"};
creat(g,s);
int n;
scanf("%d",&n);
getchar();//用来防止数后面的getline吸收掉回车
string str;
for(int i=0;i<n;i++){
getline(cin,str);
while(str.length()>4&&str.find("tret")==0)//去掉前导0
str.erase(0,5);
while(str.length()>0&&str[0]=='0'){
//去掉前导0
str.erase(str.begin());
}
if(str.length()==0)
str+='0';
if(recog(str)==1){
//如果输入的是一个数
int t=0;
for(int j=0;j<str.length();j++){
//把字符转化为数字
t=t*10+str[j]-'0';
}
cout<<res[t]<<endl;
}
else if(recog(str)==2){
//这就是个火星文
printf("%d\n",trans[str]);
}
}
return 0;
}
A1054
题意:M*N的图像,N行M个色素块,求主要的色彩(必须超过半数区域)
思路:如果二维数组暴力解的话肯定出问题,然后这一章学的又是map所以,可以尝试对应关系,目的是求出现次数最多的那个那么我觉得可以让每个色彩对应一个出现次数,bingo。
#include
#include
#include
#include
using namespace std;
map<int,int> num;
int main(){
int m,n,t;//m列,n行,t是每个块的值
scanf("%d%d",&m,&n);
for(int i=0;i<m*n;i++){
scanf("%d",&t);
if(num.find(t)!=num.end()){
//也就说找到了
num[t]++;
}
else num[t]=1;
}
map<int,int>::iterator max=num.begin();//max,表示最大值的迭代器
for(map<int,int>::iterator it=num.begin();it!=num.end();it++){
//遍历
if(max->second<it->second){
//更新max
max=it;
}
}
printf("%d\n",max->first);
return 0;
}
A1071
题意:每个人输入一句话,然后找出现最多的单词
思路:先得写一个转化函数,把大写的转化为小写的,然后还得写一个判断是否为字符函数,然后从头至尾截断判断,输出即可,但是遇到了个问题,就是string干啥啥还不方便但是对应的时候,还得用他,所以只用他对应,剩下的字符数组来干。但是发现字符数组好是好,但是不能随意变换长度,所以很可能就失败了,所以还是要用string来存放只不过内外两层循环就行了,外层循环处理句子,内层循环处理单词。
心得:
string str;
getline(cin,str);//读入string
char str2[1024];
cin.getline(str2,1024);//读入char数组
//注意不要用cin.get,他不会丢弃换行符,也不会输进去,会留在缓冲区,可能对后序造成影响
#include
#include
#include
#include
#include
#include
using namespace std;
map<string,int> num;
int judge_trans(char &w){
if(w>='0'&&w<='9'||w>='a'&&w<='z')return 1;
else if(w>='A'&&w<='Z'){
w-='A'-'a';
return 1;
}
else return 0;
}
int main(){
string str;
getline(cin,str);
int i=0;
while(i<str.length()){
string word;
while(judge_trans(str[i])&&i<str.length()){
//如果是合法字符,说明单词没结束
word+=str[i];
i++;
}
//如果不是合法字符,说明单词结束,并且要找到下一个合法字符
if(num.find(word)!=num.end())
num[word]++;
else num[word]=1;
while(i<str.length()&&!judge_trans(str[i]))
i++;
}
int max=0;//存放出现最多的单词
string ans;
for(map<string,int>::iterator it=num.begin();it!=num.end();it++){
if(max < it->second){
max=it->second;
ans=it->first;
}
}
cout<<ans<<" "<<max<<endl;
system("pause");
return 0;
}
A1022
题意:给了一本书的六条信息,一共n本书,然后,按五种查询方法输出
思路,每种信息,一个分类表,进行map,然后,查询的时候遍历就行了,关键字的提取方法和上个题的差不多,但是有一个问题,上一个题是对应出现的次数,但是这个题对应的是一组数,所以不能完全按照上个题的来,所以这里用string对应的不是string,也不是int而是set,太神奇了
注意:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
map<string,set<int>> B[6];
int judge(char w){
if(w>='0'&&w<='9'||w>='a'&&w<='z'||w>='A'&&w<='Z')return 1;
else return 0;
}
int main(){
int n;//n本书
scanf("%d",&n);
getchar();
string str;
int num;
for(int i=0;i<n;i++){
scanf("%d",&num);
getchar();
for(int j=1;j<=5;j++){
if(j==3){
//如果是3,就是关键字截取部分
string word;
char c;
while(cin>>word){
B[3][word].insert(num);
c=getchar();
if(c=='\n')break;
}
}
else {
getline(cin,str);
B[j][str].insert(num);
}
}
}
int m,on;//m次查询,on为当前查询类别
scanf("%d",&m);
getchar();
for(int i=0;i<m;i++){
getline(cin,str);
on=str[0]-'0';
cout<<str<<endl;
str.erase(0,3);
if(B[on].find(str)!=B[on].end()){
//如果找到了,就遍历set中的值即可
for(set<int>::iterator it=B[on].find(str)->second.begin();it!=B[on].find(str)->second.end();it++){
printf("%07d\n",*(it));
}
}
else printf("Not Found\n");
}
system("pause");
return 0;
}
总结:
typedef struct Node{
typename data;//数据域
int next;//指针域
}node[size];
A1032
题意:有一些单词有重复的连续字母,所以可以一起存储,给了两个首地址,还有n个结点,每个节点先是一个5位的整数,然后是他的数据,然后是下一个结点的地址,输出首个重复元素的地址,如果没有输出-1
思路:两个字符串比较,我们原来用到过哈希表,但是这个题给了的是一堆地址,所以很明显是用链表,而且是静态链表,首先是构造静态链表,构造的时候进行标记,看看原来有没有,最后按链表遍历就完事了。
#include
#include
#include
using namespace std;
struct NODE{
char w;
int next;
int tag;//tag=1的时候是第一次来,tag=2的时候是2次来
}node[100000];
int main(){
int b1,b2,n;//b1是1链开始,b2是2链开始,也用来做指针
scanf("%d%d%d",&b1,&b2,&n);
int p,q;//p用来接收当前地址,t是data,q是下一个地址
char t;
for(int i=0;i<n;i++){
scanf("%d %c %d",&p,&t,&q);
node[p].w=t;
node[p].next=q;
}
for(p=b1;p!=-1;p=node[p].next){
//先标记第一个链,如果全标记了就没有用了
node[p].tag=1;
}
for(p=b2;p!=-1;p=node[p].next){
//遍历第二个链找第一个
if(node[p].tag==1)break;
}
if(p!=-1)printf("%05d",p);
else printf("-1\n");
system("pause");
return 0;
}