pat甲组题

3.1简单模拟

A1065 A+B and C (64bit) (20 分)

由于long long 的范围为[-263,263),故题目给出的两个整数相加,根据计算机组成原理知识可能会发生正溢出或者负溢出,需进行特殊处理

#include <stdio.h>
#include <string.h>
int main() {
    long long a,b,c;
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld%lld%lld",&a,&b,&c);
        long long res=a+b;
        bool flag;
        if(a>0&&b>0&&res<0)     //正溢出
            flag=true;
        else if(a<0&&b<0&&res>=0)       //负溢出
            flag=false;
        else if(res>c)
            flag=true;
        else 
            flag=false;
        if(flag)
            printf("Case #%d: true\n",i);
        else
            printf("Case #%d: false\n",i);
    }
}

3.6字符串处理

A1001 A+B Format

这道题最好的方法是用特例法,分别列举出c的不同取值的输出情况

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
    int a,b;
    scanf("%d%d",&a,&b);
    int c=a+b;
    if(c<0){
        printf("-");
        c=-c;
    }
    if(c>=1000000){
        printf("%d,%03d,%03d",c/1000000,c%1000000/1000,c%1000);
    }
    else if(c>=1000){
        printf("%d,%03d",c/1000,c%1000);
    }
    else{
        printf("%d",c);
    }
}

A1077 Kuchiguse

此题为典型string类型的应用,特别注意的是algorithm中reverse的使用,字符串截取

#include <stdio.h>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
    int n;
    scanf("%d\n",&n);
    string ans;
    for(int i=0;i<n;i++){
        string s;
        getline(cin,s);
        int lens=s.size();
        reverse(s.begin(),s.end()); //反转字符串,从末尾开始比
        if(i==0){               //让第一个字符串作为被比较的字符串
            ans=s;
            continue;
        }
        else{
            int lenans=ans.size();
            int minlen=min(lens,lenans);
            for(int j=0;j<minlen;j++){
                if(ans[j]!=s[j]){       //截取相同的字符串
                    ans=ans.substr(0,j);
                    break;
                }
            }
        }
    }
    reverse(ans.begin(),ans.end());     //反转回字符串
    if(ans.size()==0)
        ans="nai";
    cout<<ans;
}

A1082 Read Number in Chinese

#include <stdio.h>
#include <string.h>
using namespace std;
int main() {
    char num[10][5]={"ling","yi","er","san","si","wu","liu","qi","ba","jiu"};
    char wei[5][5]={"Shi","Bai","Qian","Wan","Yi"};
    char str[15];
    scanf("%s",str);
    int len=strlen(str);
    int left=0,right=len-1; //left和right分别指向字符串首尾元素
    if(str[0]=='-'){        //如果是负数
        printf("Fu");
        left++;
    }
    while(left+4<=right){
        right-=4;           //将right每次左移4位,直到left与right在一节
    }
    while(left<len){        //循环每次处理数字的一节(4位或小于4位)
        bool flag=false;    //flag==false表示没有积累0
        bool isPrint=false; //isPrint==false表示该节没有输出过其中的位
        while(left<=right){ //从左往右处理数字某节的每一位
            if(left>0&&str[left]=='0'){ //如果当前位为0
                flag=true;
            }
            else{           //如果当前位不为0
                if(flag==true){         //如果有累计0
                    printf(" ling");
                    flag=false;
            }
            //只要不是首位(包括负号),后面的每一位前都要输出空格
            if(left>0)  printf(" ");
            printf("%s",num[str[left]-'0']);        //输出当前位数字
            isPrint=true;                           //该节至少有一位被输出
            if(left!=right){                //某节中除了个位外,都要输出十百千
                printf(" %s",wei[right-left-1]);
            }
        }
        left++;//右移一位
    }
    if(isPrint==true&&right!=len-1){    //只要不是个位,就输出万,亿
        printf(" %s",wei[(len-1-right)/4+2]);
    }
    right+=4;                       //right右移4位,输出下一字节
    }
}

4.1排序

A1012 The Best Rank

这道题细节比较多

  • 利用hash表的特性存储每个id对应学科的排名以及输出各科对应的编号
  • 利用now分别编写排序函数,再利用for循环依次求出每个id各科对应的排行
  • 进行四舍五入的方法为+0.5
  • 处理排名相同的细节情况
  • 由于题目需要按所给科目的优先级输出科目编号,最好的方法则是利用hash表的性质在求出最小值时直接求出最小值最高优先级的编号,即subject字符数组的顺序直接为优先级顺序
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
struct node{
    int id;             //学生id
    int grade[4];       //学生四门课成绩
}ans[2020];
int now;
int ranking[1000010][4]={0};        //处于存储各Id对应各科的排名
char subject[5]={'A','C','M','E'};//方便输出各科的编号
//本题的重点,利用now分别得出各科成绩的排序
bool cmp(node a,node b){        
    return a.grade[now]>b.grade[now];
}
int main() {
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=0;i<n;i++){
        scanf("%d%d%d%d",&ans[i].id,&ans[i].grade[1],&ans[i].grade[2],&ans[i].grade[3]);
        //进行四舍五入的方法为+0.5
        ans[i].grade[0]=(ans[i].grade[1]+ans[i].grade[2]+ans[i].grade[3])/3+0.5;
    }
    for(now=0;now<4;now++){//利用now对各科成绩进行排名
        sort(ans,ans+n,cmp);    
        //处理排名相同的情况
        ranking[ans[0].id][now]=1;
        for(int i=1;i<n;i++){
            if(ans[i].grade[now]==ans[i-1].grade[now])
                ranking[ans[i].id][now]=ranking[ans[i-1].id][now];
            else
                ranking[ans[i].id][now]=i+1;
        }
    }
    int query;
    for(int i=0;i<m;i++){
        scanf("%d",&query);
        if(ranking[query][0]==0){
            printf("N/A\n");
        }
        else{
            int k=0;
            //由于编号顺序及为题目所要求得优先级,故按一下方法求最小值的序列即可
            for(int j=1;j<4;j++){
                if(ranking[query][j]<ranking[query][k])
                    k=j;
            }
            printf("%d %c\n",ranking[query][k],subject[k]);
        }
    }
}

A1016 Phone Bills

定义一个结构体,并对其进行初试化

struct customer
{
	customer(){
		this->month=0;
		this->dd=0;
		this->hh=0;
		this->mm=0;
	}
	string name;//名字
	int month,dd,hh,mm;//月 天 时 分
	bool status;				//status==true表示在线,status==false表示离线
};

定义一个比较函数,按照,名字ASCII表,月时分秒进行排序

bool cmp(customer a, customer b){
	int x = a.name.compare(b.name);
	if(x!=0){
		return x<0;
	}else if(a.month!=b.month)
	{
		return a.month<b.month;
	}else if (a.dd!=b.dd)
	{
		return a.dd<b.dd;
	}else if(a.hh!=b.hh){
		return a.hh<b.hh;
	}else
	{
		return a.mm<b.mm;
	}
}

定义一个计算费率和时间的函数,其中time用指针传递,使得其值能够被改变,函数即让小的时间随分钟递增直至大于大的时间

double get_ans(customer a, customer b,int& time){
	int money=0;
	while (a.dd>b.dd||a.hh>b.hh||a.mm>b.mm)		//当天时分都不大于时,才说明两者时间相同
	{
		time++;			//按分钟计算费率,依次递增					
		money+=prize[b.hh];
		b.mm++;
		if (b.mm>=60)
		{
			b.mm=0;
			b.hh++;
		}
		if (b.hh>=24)
		{
			b.hh=0;
			b.dd++;
		}
	}
	return money/100.0;
}

用on以及off分别表示在线和离线的下标,用temp表示上一个结点,flag表示当前用户是否已经输出了名字信息,flag==false表示没有输出,对其进行初始化。并从第二个结点开始进行for循环

    customer temp;		
	temp=data[0];			//将上一个结点令为第一个
	int on=-1,off;			
	bool flag=false;
	if (temp.status)
	{
		on=0;
	}

判断当前结点属于的用户是否与上一结点的用户是否相同,用compare方法进行比较
若相同则实时更新on和off,若满足off==on+1,则进行输出
若不相同则输出总的费用值
每次循环结束都重新更新temp以及on,off若到了最后一个结点,则直接输出总的费用

4.4贪心算法

A1038 Recover the Smallest Number

本题的核心策略就是:对数字串S1与S2,如果有S1+S2<S2+S1(加号表示拼接),那么就把s1放在前面;否则,就把S2放在S1前面

#include <stdio.h>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn=100010;
string str[maxn];
bool cmp(string a,string b){        //贪心算法的核心
    return a+b<b+a;
}
int main(){
    int n;
    scanf("%d",&n);                 
    for(int i=0;i<n;i++)
        cin>>str[i];
    sort(str,str+n,cmp);            
    string ans; 
    for(int i=0;i<n;i++)            //将字符串拼接起来
        ans+=str[i];
    while(ans.size()!=0&&ans[0]=='0')			//去除前导0
        ans.erase(ans.begin());
    if(ans.size()==0)
        cout<<0;
    else
        cout<<ans;
}

4.5二分

A1044 Shopping in Mars

这道题有以下几个比较重要的点:
1.用到了很重要的编程思想,即用一个sum数组从而使得距离得以递增,从点i到j的距离即为sum[j]-sum[i-1];
2.利用二分法求数组中第一个大于x值的位置的函数写法,值得注意的是,返回的值是大于x的,故所求右边界可能为所得点的上一个点

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int main(){
    int n,m;
    scanf("%d %d",&n,&m);
    int sum[100010];
    sum[0]=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&sum[i]);
        sum[i]+=sum[i-1];       //sum数组用于存储远点到各点的距离,显然为递增数组
    }
    int nearm=1000000000;
    //此轮for循环用于寻找大于m但最接近于m或直接等于m的的序列和值
    for(int i=1;i<=n;i++){
        //lower_bound(first,last,val)用于寻找在数组或容器的[first,last)
        //范围内第一个值大于等于val的元素的位置
        int j=lower_bound(sum+i,sum+n+1,m+sum[i-1])-sum;
        if(sum[j]-sum[i-1]==m){
            nearm=m;
            break;
        }
        else if(j<=n&&sum[j]-sum[i-1]<nearm)
            nearm=sum[j]-sum[i-1];
    }
    //寻找所有满足条件的序列和
    for(int i=1;i<=n;i++){
        int j=lower_bound(sum+i,sum+n+1,nearm+sum[i-1])-sum;
        if(sum[j]-sum[i-1]==nearm){
            printf("%d-%d\n",i,j);
        }
    }
}

4.6 two pointers

A1089 Insert or Merge

本题的关键是插入排序和归并排序的关键步骤,其他只需保证逻辑上的正确

#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int maxn=110;
int ori[maxn],temp[maxn],change[maxn],n;
bool isequ(){               //判断两个数组是否相等
    for(int i=0;i<n;i++)
        if(change[i]!=temp[i])
            return false;
    return true;
}
void showarray(){           //输出数组
    for(int i=0;i<n;i++){
        if(i!=0)
            printf(" ");
        printf("%d",temp[i]);
    }
}
bool insertion(){
    bool flag=false;
    for(int i=1;i<n;i++){
        if(i!=1&&isequ()){
            flag=true;
        }
        int tempp=temp[i],j=i;
        while(j>0&&temp[j-1]>tempp){        //关键步骤:进行一趟插入排序
            temp[j]=temp[j-1];
            j--;
        }
        temp[j]=tempp;
        if(flag){
            return true;
        }
    }
    return false;
}
void mergion(){
    bool flag=false;
    for(int step=2;step/2<=n;step*=2){
        if(step!=2&&isequ()){
            flag=true;
        }
        for(int i=0;i<n;i+=step)        //关键步骤,进行一趟归并排序
            sort(temp+i,temp+min(i+step,n));
        if(flag){
            showarray();
            return;
        }
    }
}
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&ori[i]);
        temp[i]=ori[i];
    }
    for(int i=0;i<n;i++)
        scanf("%d",&change[i]);
    if(insertion()){        //如果是插入排序
        printf("Insertion Sort\n");
        showarray();
    }
    else{                   //否则是归并排序
        for(int i=0;i<n;i++)    //还原temp数组
            temp[i]=ori[i];
        printf("Merge Sort\n");     
        mergion();
    }
}

4.7其他高效技巧与算法

A1101 Quick Sort

这道题应用了很重要的编程思想,即依靠一个leftmaxnum数组从左边第二个元素叠加计算出各元素左边最大的元素,同理从右边倒数第二个元素起依次叠加计算出各元素右边最小的元素,从而判断各元素是否为主元

#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int maxn=100010;
const int inf=1000000001;       //定义一个无穷大
int main(){
    int ans[maxn];
    int num[maxn]={0};
    int cnt=0;
    int leftmaxnum[maxn]={0};
    int rihgtminnum[maxn]={0};
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&num[i]);
    for(int i=1;i<n;i++){           //从第二个元素起依次叠加记录左边元素的最大值
        leftmaxnum[i]=max(leftmaxnum[i-1],num[i-1]);
    }
    for(int i=n-1;i>=0;i--){
        if(i==n-1)
            rihgtminnum[i]=inf;
        else{                       //从倒数第二个元素起依次叠加记录右边元素的最小值
            rihgtminnum[i]=min(rihgtminnum[i+1],num[i+1]);
        }
    }
    for(int i=0;i<n;i++){
        if(num[i]>leftmaxnum[i]&&num[i]<rihgtminnum[i])
            ans[cnt++]=num[i];
    }
    printf("%d\n",cnt);
    for(int i=0;i<cnt;i++){
        if(i!=0)
            printf(" ");
        printf("%d",ans[i]);
    }
    printf("\n");
}

5.4素数

A1078 Hashing

此题的重点有以下
1.判断一个数是否为素数的函数写法

bool isPrime(int x){        //判断一个数是否为质数
    if(x<=1)
        return false;
    int sqr=(int)sqrt(1.0*x);
    for(int i=2;i<=sqr;i++){
        if(x%i==0)
            return false;
    }
    return true;
}

2.平方探查法的写法

 int step;                     
            //否则利用平方探查法进行查找是否有空位,(题目要求只查找正向即可)
            for(step=1;step<m;step++){
                int z=(temp%m+step*step)%m;
                if(hashtable[z]==false){
                    hashtable[z]=true;
                    printf("%d",z);
                    break;
                }
            }
            if(step>=m){                    //若平方探查法最后没有找到空位
                printf("-");
            }

6.2 set的常见用法详解

A1063 Set Similarity

此题是一道经典的set容器使用案例,需要关注的重点即是,如何对set容器进行遍历,find函数以及insert函数的使用

#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <set>
#include <iostream>
using namespace std;
const int N=51;
set<int> st[N];
void compare(int x,int y){
    int totalnum=st[y].size(),samename=0;           //总的数目则是两个集合的并集,相同数目则是交集
    for(set<int>::iterator it=st[x].begin();it!=st[x].end();it++){
    //便利iterator的方式
        if(st[y].find(*it)!=st[y].end())
        //若在另一个集合中发现该元素
            samename++;
        else
        //若没有在另一个集合中发现该元素
            totalnum++;
    }
    printf("%.1f%%\n",samename*100.0/totalnum);
}
int main(){
    int n,m,x,k,query1,query2;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&m);
        for(int j=0;j<m;j++){
            scanf("%d",&x);
            st[i].insert(x);
        }
    }
    scanf("%d",&k);
    for(int i=0;i<k;i++){
        scanf("%d %d",&query1,&query2);
        compare(query1,query2);
    }
}

A1022 Digital Library

此题是一道综合性比较强的题重点在于
1.输入输出格式的控制
2.map<string,set >这种复合容器类型的使用

#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <map>
#include <string>
#include <set>
#include <iostream>
using namespace std;
//5个map变量分别建立书名,作者,关键词,出版社以及出版年份与id的映射关系
map<string,set<int> > mptitle,mpauthoe,mpkey,mppub,mpyear;
//如果单独把查询操作提炼成一个函数,那么一定要对参数使用引用,否则最后
//一组数据会超时。由此可见,字符串以及map的参数传递速度比较慢,如果需要作为
//函数的参数的话,需要尽可能加上引号
void query(map<string,set<int> >& mp,string& str){
    if(mp.find(str)==mp.end())      //找不到
        printf("Not Found\n");
    else{
        //输出set容器内所有的string类型
        for(set<int>::iterator it=mp[str].begin();it!=mp[str].end();it++){
            printf("%07d\n",*it);
        }
    }
}
int main(){
   int n,m,id,type;
   string title,author,key,pub,year;
   scanf("%d",&n);
   for(int i=0;i<n;i++){
        scanf("%d",&id);
        getchar();      //接收Id后面的换行
        getline(cin,title);
        mptitle[title].insert(id);
        getline(cin,author);
        mpauthoe[author].insert(id);
        while(cin>>key){
            mpkey[key].insert(id);
            char c=getchar();   //接收c判断是否为回车
            if(c=='\n')         //若是回车则说明已到行末则退出
                break;
        }
        getline(cin,pub);
        mppub[pub].insert(id);
        getline(cin,year);
        mpyear[year].insert(id);
   }
   string temp;
   scanf("%d",&m);
   for(int i=0;i<m;i++){
        scanf("%d: ",&type);
        getline(cin,temp);
        cout<<type<<": "<<temp<<endl;
        if(type==1)         //按查询类型输出
            query(mptitle,temp);
        else if(type==2)
            query(mpauthoe,temp);
        else if(type==3)
            query(mpkey,temp);
        else if(type==4)
            query(mppub,temp);
        else
            query(mpyear,temp);
    }
}

7.1栈的应用

A1051 Pop Sequence

此题涉及许多栈的基本操作,例如入栈出栈,判断栈空等,应作为代码模型加以熟练

#include <stack>
#include <stdio.h>
using namespace std;
const int maxn=1010;
int arr[maxn];      //定义一个数组存储输入的序列
stack<int> st;
int main(){
   int m,n,T;
   scanf("%d%d%d",&m,&n,&T);
   while(T--){      //循环T次
        while(!st.empty()){     //将栈元素全部清空
            st.pop();
        }
        for(int i=1;i<=n;i++)   //将输入序列扫面入数组
            scanf("%d",&arr[i]);
        int current=1;          //指向出栈序列中待出栈的元素
        bool flag=true;
        for(int i=1;i<=n;i++){
            st.push(i);         //按1~n的顺序压入栈
            if(st.size()>m){    //若此时栈中元素个数大于容量m,则序列非法
                flag=false;
                break;
            }
            //如果栈顶元素与出栈序列当前位置的元素相同时
            while(!st.empty()&&st.top()==arr[current]){    
                st.pop();       //反复弹栈并令current++
                current++;
            }
        }
        if(st.empty()==true&&flag==true){
            printf("YES\n");    //栈空且flag==true时表明合法
        }
        else
            printf("NO\n");
   }
}

7.2队列的应用

A1056 Mice and Rice

这道题的核心思想是,首先将所有老鼠入队,每只比较完的老鼠则让他出队,让每组胜出的老鼠继续入队,进行循环比较,直至队内的老鼠只剩下一只为止

#include <stack>
#include <stdio.h>
#include <queue>
using namespace std;
const int maxn=1010;
struct node{
    int weight;     //记录每只老鼠的体重
    int R;          //排行
}mice[maxn];
int main(){
   int np,ng,order;
   scanf("%d %d",&np,&ng);
   for(int i=0;i<np;i++)          //按序输入每只老鼠的体重
        scanf("%d",&mice[i].weight);
   queue<int> q;
   for(int i=0;i<np;i++){       //将排序依次推入队列
        scanf("%d",&order);
        q.push(order);
   }
   int temp=np,group;           //用temp记录当前排序总的老鼠数,group记录总组数
   while(q.size()!=1){          //当只剩下最后一只老鼠时输出
        if(temp%ng==0)          //如果刚好够够分
            group=temp/ng;
        else                    //不够分就多加一组
            group=temp/ng+1;
        for(int i=0;i<group;i++){
            int k=q.front();    //用k记录当前组最重老鼠的序号
            for(int j=0;j<ng;j++){
                if(i*ng+j>=temp)     //如果已经输出完了则直接break
                    break;
                int front=q.front();    //依次取队头元素
                if(mice[front].weight>mice[k].weight)
                    k=front;          //寻找最大值
                mice[front].R=group+1;  //置出队元素为组数+1
                q.pop();                //队头元素出队
            }
            q.push(k);                  //当前组最重老鼠从队末入队
        }
        temp=group;                     //新一轮总老鼠数即为上一轮组数
   }
   mice[q.front()].R=1;                 //最后一只老鼠排名为第一
   for(int i=0;i<np;i++){               //依次输出
        if(i!=0)
            printf(" ");
        printf("%d",mice[i].R);
   }
}

7.3链表处理

A1074 Reversing Linked List

此题涉及到使用静态链表的核心操作,应作为代码模型记住

struct Node{            //定义一个静态链表
    int address,data,next;
    int order;				//用order存储链表结点的顺序
}node[maxn];
bool cmp(Node a,Node b){
    return a.order<b.order;
}
int main() {
  for(int i=0;i<maxn;i++){
    node[i].order=maxn;         //让所有的次序为无效
  }
  int begin,N,K;
  scanf("%d%d%d",&begin,&N,&K); //输出开始位置,结点总个数,以及正整数K
  for(int i=0;i<N;i++){         //依次输入结点
    int addrsss;
    scanf("%d",&addrsss);
    scanf("%d%d",&node[addrsss].data,&node[addrsss].next);
    node[addrsss].address=addrsss;
  }
  int p=begin,count=0;          
  while(p!=-1){                 //从开始结点到最后一个结点依次用order编号
    node[p].order=count++;
    p=node[p].next;
  }
  sort(node,node+maxn,cmp);     //根据order编号进行排序
  }

8.1深度优先算法(DFS)

A1103 Integer Factorization

#include <stack>
#include <stdio.h>
#include <algorithm>
#include <vector>
using namespace std;
//maxFacSum记录最大底数和
int n,k,p,maxFacSum=-1;
//fac记录0^1,1^p...i^p,使得i^p为不超过n的最大数
//ans记录最优底数序列,temp存放递归中的临时底数序列
vector<int> fac,ans,temp;
//power计算x^p
int power(int x){
    int ans=1;
    for(int i=0;i<p;i++){
        ans*=x;
    }
    return ans;
}
//init预处理fac数组,注意把0也存进去
void init(){
    int i=0,temp=0;
    while(temp<=n){
        fac.push_back(temp);
        temp=power(++i);
    }
}
//DFS函数,当前访问fac[index],nowW为当前选中的个数
//sum为当前选中的数之和,facSum为当前选中的底数之和
void DFS(int index,int nowW,int sum,int facSum){
    if(sum==n&&nowW==k){        //找到一个满足的序列
        if(facSum>maxFacSum){   //如果底数之和更优
            ans=temp;           //更新最优序列
            maxFacSum=facSum;   //更新最大底数之和
        }
        return;
    }
    if(sum>n||nowW>k)           //这种情况不会产生答案直接返回
        return;
    if(index-1>=0){                //fac[0]不需要选择
        temp.push_back(index);      //把底数Index加入临时序列temp
        DFS(index,nowW+1,sum+fac[index],facSum+index);  //"选"的分支
        temp.pop_back();           //选的分支结束后把刚加进去的数pop掉
        DFS(index-1,nowW,sum,facSum);   //不选的分支
    }
}
int main(){
   scanf("%d%d%d",&n,&k,&p);
   init();
   DFS(fac.size()-1,0,0,0);     //从fac的最后一位开始往前搜索
   if(maxFacSum==-1)
        printf("Impossible");
   else{
        printf("%d = %d^%d",n,ans[0],p);
        for(int i=1;i<ans.size();i++){
            printf(" + %d^%d",ans[i],p);
        }
   }
}

9.2二叉树的遍历

A1102 Invert a Binary Tree

这是一道使用静态树的典型例题

#include <stack>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
int n;
struct Node{            //定义一个静态结点类型
    int left,right;
}node[10];
bool hashtable[10]={false};     //用于标记每个元素是否出现过,没出现的即为头结点
//(重点)后序遍历用于交换左孩子与右孩子
void postOrder(int x){
    if(x==-1)
        return;
    if(node[x].left!=-1)
        postOrder(node[x].left);
    if(node[x].right!=-1)
        postOrder(node[x].right);
    swap(node[x].left,node[x].right);
}
//输出层次遍历
void levelOrder(int root){
    queue<int> q;
    q.push(root);
    int cnt=0;
    while(!q.empty()){
        root=q.front();
        if(cnt!=0)
            printf(" ");
        printf("%d",root);
        q.pop();
        if(node[root].left!=-1)
            q.push(node[root].left);
        if(node[root].right!=-1)
            q.push(node[root].right);
        cnt++;
    }
}
//输出中序遍历
int k=0;
void inOrder(int root){
    if(root==-1)
        return;
    inOrder(node[root].left);
    if(k!=0)
        printf(" ");
    k++;
    printf("%d",root);
    inOrder(node[root].right);
}
int main(){
   scanf("%d",&n);
   getchar();       //接受回车
   char temp1,temp2;
   for(int i=0;i<n;i++){
        scanf("%c %c",&temp1,&temp2);
        getchar();      //接受回车
        if(temp1>='0'&&temp1<='9'){
            hashtable[temp1-'0']=true;
            node[i].left=temp1-'0';
        }
        else{
            node[i].left=-1;
        }
        if(temp2>='0'&&temp2<='9'){
            hashtable[temp2-'0']=true;
            node[i].right=temp2-'0';
        }
        else{
            node[i].right=-1;
        }
   }
   int root;
   for(int i=0;i<n;i++)     //寻找头结点
        if(hashtable[i]==false)
            root=i;
    postOrder(root);
    levelOrder(root);
    printf("\n");
    inOrder(root);
}

A1086 Tree Traversals Again

这是一道使用动态树的典型例题

#include <stack>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <iostream>
using namespace std;
struct node{            //定义一个结点类型
    int data;
    node* lchild;
    node* rchild;
};
int pre[32],in[32];
//利用中序遍历以及前序遍历创建一个二叉树的过程
node* create(int preL,int preR,int inL,int inR){
    if(preL>preR)
        return NULL;
    node* root=new node;
    root->data=pre[preL];
    int k;
    for(k=inL;k<=inR;k++)
        if(in[k]==pre[preL])
            break;
    int numLeft=k-inL;
    root->lchild=create(preL+1,preL+numLeft,inL,k-1);
    root->rchild=create(preL+numLeft+1,preR,k+1,inR);
    return root;
}
//后序遍历输出二叉树
int cnt=0;
void postOrder(node* root){
    if(root==NULL)
        return;
    postOrder(root->lchild);
    postOrder(root->rchild);
    if(cnt!=0)
        printf(" ");
    printf("%d",root->data);
    cnt++;
}
int main(){
   int n;
   scanf("%d",&n);
   int x=0,y=0;
   stack<int> s;
    char oper[5];
   for(int i=0;i<2*n;i++){
        int temp;
        scanf("%s",oper);
        if(strcmp(oper,"Push")==0){
            scanf("%d",&temp);
            pre[x++]=temp;
            s.push(temp);
        }
        else{
            in[y++]=s.top();
            s.pop();
        }
   }
   node* root=create(0,n-1,0,n-1);
   postOrder(root);
}

9.3树的遍历

A1106 Lowest Price in Supply Chain

#include <stack>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <vector>
#include <math.h>
using namespace std;
const int maxn=100010;
vector<int> child[maxn];
int mindepth=maxn,num=0;
void DFS(int index,int depth){
    if(child[index].size()==0){
        if(depth<mindepth){
            num=1;
            mindepth=depth;
        }
        else if(depth==mindepth)
            num++;
        return;
    }
    for(int i=0;i<child[index].size();i++)
        DFS(child[index][i],depth+1);
}
int main(){
    int n;
    double p,r;
    scanf("%d %lf %lf",&n,&p,&r);
    for(int i=0;i<n;i++){
        int m,temp;
        scanf("%d",&m);
        if(m!=0){
            for(int j=0;j<m;j++){
                scanf("%d",&temp);
                child[i].push_back(temp);
            }
        }
    }
    DFS(0,0);
    printf("%.4f %d",p*pow(1+r/100,mindepth),num);
}

9.4二叉查找树(BST)

A1043 Is It a Binary Search Tree

#include <stdio.h>
#include <string.h>
#include <vector>
using namespace std;
struct node{        //定义链表结点类型
    int data;
    node *left,*right;
};
//插入操作
void insert(node* &root,int data){
    if(root==NULL){
        root=new node;
        root->data=data;
        root->left=root->right=NULL;
        return;
    }
    if(data<root->data)
        insert(root->left,data);
    else
        insert(root->right,data);
}
//先序遍历,结果存在vi中
void preorder(node* root,vector<int>&vi){
    if(root==NULL)
        return;
    vi.push_back(root->data);
    preorder(root->left,vi);
    preorder(root->right,vi);
}
//镜像树先序遍历,结果存于vi
void preorderMirror(node* root,vector<int>&vi){
    if(root==NULL)
        return;
    vi.push_back(root->data);
    preorderMirror(root->right,vi);
    preorderMirror(root->left,vi);
}
//后序遍历,结果存于vi
void postorder(node* root,vector<int>&vi){
    if(root==NULL)
        return;
    postorder(root->left,vi);
    postorder(root->right,vi);
    vi.push_back(root->data);
}
//镜像树后序遍历,结果存于vi
void postorderMirror(node* root,vector<int>&vi){
    if(root==NULL)
        return;
    postorderMirror(root->right,vi);
    postorderMirror(root->left,vi);
    vi.push_back(root->data);
}
vector<int> origin,pre,preM,post,postM;
int main(){
    int n,data;
    node* root=NULL;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&data);
        origin.push_back(data);
        insert(root,data);
    }
    preorder(root,pre);
    preorderMirror(root,preM);
    postorder(root,post);
    postorderMirror(root,postM);
    if(origin==pre){
        printf("YES\n");
        for(int i=0;i<post.size();i++){
            if(i!=0)
                printf(" ");
            printf("%d",post[i]);
        }
    }
    else if(origin==preM){
        printf("YES\n");
        for(int i=0;i<postM.size();i++){
            if(i!=0)
                printf(" ");
            printf("%d",postM[i]);
        }
    }
    else
        printf("NO");
}

A1064 Complete Binary Search Tree

本题的核心是,完全二叉树可以存放在数组中,若某结点的编号为x,那么其左孩子结点的编号一定是2x,右孩子结点的编号一定是2x+1

#include <stdio.h>
#include <algorithm>
using namespace std;
const int maxn=1010;
int n,number[maxn],CBT[maxn],index=0;

void inorder(int root){
    if(root>n)          //空节点,直接返回
        return;
    inorder(root*2);    //往左子树递归    
    CBT[root]=number[index++];  //根节点处赋值umber[index]
    inorder(root*2+1);          //向右子树递归
}

int main(){
    scanf("%d",&n);             
    for(int i=0;i<n;i++){
        scanf("%d",&number[i]);
    }
    sort(number,number+n);
    inorder(1);
    for(int i=1;i<=n;i++){
        printf("%d",CBT[i]);
        if(i<n)
            printf(" ");
    }
}

10.3图的遍历

A1013 Battle Over Cities

本题的思路是通过计算出图中连接块的个数,然后求得需要加的边。由于图的遍历过程中总是每次访问单个连通块,并将该连通块内所有的顶点都标记为已访问,然后去访问下一个连通块。

#include <stdio.h>
#include <string.h>
#include <vector>
#include <string>
using namespace std;
const int N=1111;
vector<int> G[N];       //邻接表
bool vis[N];            //标记顶点i是否已被访问
int currentpoint;       //当前需要删除的顶点编号
//dfs(v)遍历顶点v所在的连通块
void dfs(int v){
    if(v==currentpoint)
        return;
    vis[v]=true;
    for(int i=0;i<G[v].size();i++)
        if(vis[G[v][i]]==false)
            dfs(G[v][i]);
}
int n,m,k;
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=0;i<m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        G[a].push_back(b);                //建立无向图的领接表
        G[b].push_back(a);
    }
    for(int query=0;query<k;query++){
        scanf("%d",&currentpoint);        //扫描每个待查询点作为当前点
        memset(vis,false,sizeof(vis));    //每次查询初始化标记矩阵
        int block=0;                      //统计连接块的数目
        for(int i=1;i<=n;i++){            
            if(i!=currentpoint&&vis[i]==false){
                dfs(i);
                block++;
            }
        }
        printf("%d\n",block-1);             //连接块减1即是需要加的边
    }   
}

A1034 Head of a Gang

#include <stdio.h>
#include <string.h>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn=2010;
const int inf=1000000000;

int G[maxn][maxn]={0},weight[maxn]={0};
int numperson=0;
int n,k,id1,id2,cost;
bool vis[maxn]={false};
map<string,int> strtoint;
map<int,string> inttostr;
map<string,int> gang;
//dfs函数访问单个连通块
void dfs(int nowvisit,int &head,int &totalvalue,int &nowmember){
     //每访问一个成员
    nowmember++;
    vis[nowvisit]=true;
    //更新head
    if(weight[nowvisit]>weight[head])
        head=nowvisit;
    for(int i=0;i<numperson;i++){
        //如果能从nowvisit到达i
        if(G[nowvisit][i]>0){
            totalvalue+=G[nowvisit][i];     //更新连通块的totalvalue
            G[nowvisit][i]=G[i][nowvisit]=0;        //删除这条边防止回头
            if(vis[i]==false){              //如果i未被访问,则递归访问i
                dfs(i,head,totalvalue,nowmember);
            }
        }
    }
}
//dfstrave函数遍历整个图,获得每个连通块的信息
void dfstrave(){
    for(int i=0;i<numperson;i++){           //枚举所有人
        if(vis[i]==false){
            int head=i,totalvalue=0,nowmember=0;
            dfs(i,head,totalvalue,nowmember);       //遍历i所在的连通块
            if(nowmember>2&&totalvalue>k)           //更新gang
                gang[inttostr[head]]=nowmember;
        }
    }
}

int change(string s){
    if(strtoint.find(s)!=strtoint.end()){
        return strtoint[s];
    }
    else{
        strtoint[s]=numperson;
        inttostr[numperson]=s;
        return numperson++;
    }
}
int main(){
    string s1,s2;
    scanf("%d %d",&n,&k);
    for(int i=0;i<n;i++){
        cin>>s1>>s2>>cost;
        id1=change(s1);
        id2=change(s2);
        G[id1][id2]+=cost;
        G[id2][id1]+=cost;
        weight[id1]+=cost;
        weight[id2]+=cost;
    }
    dfstrave();
    cout<<gang.size()<<endl;
    for(map<string,int>::iterator it=gang.begin();it!=gang.end();it++)
        cout<<it->first<<" "<<it->second<<endl;
}

10.4最短路径

A1030 Travel Plan

本题是应用dijkstra算法解决问题的典型模板。首先利用dijkstra算法求解最优路径,并存储在pre数组中,再利用dfs算法对pre数组进行遍历求出题目所求答案。

#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
const int maxv=510;
const int inf=1000000000;

int n,m,st,ed,G[maxv][maxv],cost[maxv][maxv];
int d[maxv],mincost=inf;
bool vis[maxv]={false};
vector<int> pre[maxv];      //前驱
vector<int> tempPath,path;  //临时路径及最优路径

void dijkstra(int s){       //s为起点
    fill(d,d+maxv,inf);     //fill函数将整个d数组赋为inf
    d[s]=0;                 //起点s到达自身的距离为0
    for(int i=0;i<n;i++){   //循环n次
        int u=-1,MIN=inf;   //u使d[u]最小,MIN存放该最小的d[u]
        for(int j=0;j<n;j++){   //找到未访问的顶点中d[]最小的
            if(vis[j]==false&&d[j]<MIN){
                u=j;
                MIN=d[j];
            }
        }
        //找不到小于inf的d[u],说明剩下的顶点和起点s不连通
        if(u==-1)
            return;
        vis[u]=true;        //标记u为已访问
        for(int v=0;v<n;v++){
            //如果v未访问&&u能到达v
            if(vis[v]==false&&G[u][v]!=inf){
                if(d[u]+G[u][v]<d[v]){
                    d[v]=d[u]+G[u][v];      //以u为中介点使d[v]更小
                    pre[v].clear();         //优化d[v]
                    pre[v].push_back(u);    //u为v的前驱
                }
                else if(d[u]+G[u][v]==d[v]){    //找到相同长度的路径
                    pre[v].push_back(u);    //u为v的前驱之一
                }
            }
        }
    }
}

void dfs(int v){
    if(v==st){                          //递归边界,到达叶子结点(路径起点)
        tempPath.push_back(v);
        int tempCost=0;                 //记录当前路径的花费之和
        for(int i=tempPath.size()-1;i>0;i--){       //倒着访问
            int id=tempPath[i],idNext=tempPath[i-1];    //当前结点及下个结点
            tempCost+=cost[id][idNext];             //增加id->inNext的边权
        }
        if(tempCost<mincost){           //如果当前路径的边权之和更小
            mincost=tempCost;
            path=tempPath;
        }
        tempPath.pop_back();
        return;
    }
    tempPath.push_back(v);
    for(int i=0;i<pre[v].size();i++)
        dfs(pre[v][i]);
    tempPath.pop_back();
}
int main(){
    scanf("%d%d%d%d",&n,&m,&st,&ed);
    int u,v;
    fill(G[0],G[0]+maxv*maxv,inf);      //初始化图G
    fill(cost[0],cost[0]+maxv*maxv,inf);
    for(int i=0;i<m;i++){
        scanf("%d%d",&u,&v);
        scanf("%d%d",&G[u][v],&cost[u][v]);
        G[v][u]=G[u][v];
        cost[v][u]=cost[u][v];
    }
    dijkstra(st);                       //dijkstra算法入口
    dfs(ed);                            //获得最优路径
    for(int i=path.size()-1;i>=0;i--)
        printf("%d ",path[i]);          //倒着输出路径上的结点
    printf("%d %d\n",d[ed],mincost);      //最短距离、最短路径上的最小花费
}

13.3 快乐模拟

A1105 Spiral Matrix

#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
#include <math.h>
using namespace std;
int ans[10010][10010];
bool cmp(int a,int b){
    return a>b;
}
int main(){
    int n;
    int arr[10010];
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&arr[i]);
    sort(arr,arr+n,cmp);
    //求出题目所要求的的x,y
    int x=(int)ceil(sqrt(1.0*n));   
    while(n%x!=0){
        x++;
    }
    int y=n/x;
    //初始化
    int left=1,up=1,i=1,j=1,right=y,down=x;
    int k=0;
    //填充整个数组
    while(k<n){
        while(k<n&&j<right){
            ans[i][j]=arr[k++];
            j++;
        }
        while(k<n&&i<down){
            ans[i][j]=arr[k++];
            i++;
        }
        while(k<n&&j>left){
            ans[i][j]=arr[k++];
            j--;
        }
        while(k<n&&i>up){
            ans[i][j]=arr[k++];
            i--;
        }
        up++,down--,left++,right--;
        i++,j++;
        if(k==n-1){
            ans[i][j]=arr[k++];
        }
    }
    //输出
    if(n==1){
        printf("%d",arr[0]);
        return 0;
    }
    for(int i=1;i<=x;i++){
        for(int j=1;j<=y;j++){
            printf("%d",ans[i][j]);
            if(j<y)
                printf(" ");
            else
                printf("\n");
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值