PAT甲级(advanced level)题目解答记录

目录

1001.A+B Format

1002.A+B for Polynomials

1003.Emergency

1020.Tree Traversals

1030.Travel Plan


1001.A+B Format

题目概述:

计算两个数字之和,输出时表示为每隔3位以逗号分隔的“标准形式”。

解题思路:

首先考虑int的取值范围。int一般为4个字节,即32位,能表示的数的范围是-2147483648~2147483647,而题目给出的范围在这个之内,因此可以直接输入整型。

在输出的时候,分三种情况。

当sum>999时需要输出逗号。先将数字转为string,然后从个位开始,每三位得一个逗号,一位一位倒序保存入新得string变量。然后将这个string进行反转操作之后输出。

当sum<-999时,先取绝对值,然后操作同上,最后在string变量中补一个负号,然后反转,输出。

当sum介于-999-999时,直接输出即可,不用加逗号。

#include <iostream>
#include <cmath>
#include <algorithm>

using namespace std;
int getlenth(int num)
{
    int n = 0;
    while(num!=0)
    {
        num /= 10;
        ++n;
    }
    return n;
}
int main()
{
    int a,b;
    cin>>a>>b;
    int sum = a + b;
    if(sum >= -999 && sum <= 999)
    {
        cout<<sum<<endl;
    }
    else if(sum < -999)
    {
        int tmp = abs(sum);
        int weishu = getlenth(tmp);
        //cout<<tmp<<" "<<weishu<<endl;
        string str = to_string(tmp);

        string out;
        for(int i = weishu - 1,pos = 0;i >= 0;--i,++pos)
        {
            if(pos != 0 && pos % 3 == 0)
            {
                out += ',';
            }
            out += str[i];
        }
        out +='-';
        reverse(out.begin(),out.end());
        cout<<out<<endl;
    }
    else if(sum > 999)
    {
        string str = to_string(sum);
        int weishu = getlenth(sum);
        string out;
        for(int i = weishu - 1,pos = 0;i >= 0;--i,++pos)
        {
            if(pos != 0 && pos % 3 == 0)
            {
                out += ',';
            }
            out += str[i];
        }
        reverse(out.begin(),out.end());
        cout<<out<<endl;
    }

    return 0;
}

 


1002.A+B for Polynomials

题目概述:

计算两个多项式之和,一行中给出第一个数k,代表多项式的非零项的个数,之后依次给出x的次数和系数,且为降幂排序给出。输出要在一行中,第一个数表示求和之后非零项的数目k,然后依次降序给出次数和系数,并且系数要保留一位小数。

解题思路:

题目中给出了次数的取值范围为0-1000,因此可以用两个1001维的数组保存两个多项式,这样子直接对应位求和即可得出求和之后的多项式放入数组中,然后先输出结果多项式的非零项数目k,然后依次输出非零的多项式的次数和系数。

控制输出小数位数的函数为setprecision(),定义在头文件<iomanip>中。

#include <iostream>
#include <vector>
#include <iomanip>

using namespace std;

int main()
{
    int N1,N2;
    double p1[1001] = {0};
    double p2[1001] = {0};
    cin>>N1;
    for(int i = 0;i < N1;++i)
    {
        int exp;
        double coe;
        cin>>exp>>coe;
        p1[exp] = coe;
    }
    cin>>N2;
    for(int i = 0;i < N2;++i)
    {
        int exp;
        double coe;
        cin>>exp>>coe;
        p2[exp] = coe;
    }
    vector<double> summ;
    for(int i = 0;i < 1001;++i)
    {

        if(p1[i] + p2[i] == 0)
            continue;
        summ.push_back(p1[i] + p2[i]);
    }
    cout<<summ.size();
    for(int i = 1000;i >= 0;--i)
    {
        if(p1[i] + p2[i] == 0)
            continue;
        cout<<' '<<i<<' '<<fixed<<setprecision(1)<<p1[i] + p2[i];
    }
    return 0;
}


1003.Emergency

题目概述:

此题未dijkstra算法的典型应用,套用即可。

题目解析:

需要注意一些细节。

(1)注意函数的作用域,因此在函数外使用内置类型的函数fill()时会报错的,全局变量可以在函数外定义,但是用fill函数初始化的时候必须在某个函数(某个作用域)中进行。

(2)注意数组的初始化,二维数组使用fill初始化的时候,设定的数组大小为N*N,n为实际的点的个数,如果初始化时用

fill(map[0],map[0]+n*n),想法是只初始化一个n*n的矩阵,而实际上却是初始化了map的[0][0]~[0][n*n],也就是二维数组的第一行的前n*n个值,这就是严重的错误。因此在代码中直接将整个数组都初始化为一个很大的数即可。

(3)定义全局变量的数组时,数组的index必须是常量const 值,可以:int d[6],或者const int N=500,然后int d[N]。但是index不能是int,必须是const int。

dijkstra算法思路:

d[]保存最短距离,s为起始点。

D(d[],s)
{
    初始化起点;
    for(n次)
    {
        u = d[u]中最小的且未被访问过的点的编号;
        标记u已经被访问过;
        for(从u出发能到达的所有点v)
        {
            if(v未被访问过&&以u为中介点能使s到v的最短距离d[v]更优;
                优化d[v];
        }
    }
}

 

具体dijkstra算法在代码注释中。

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 500;//最多有N个点
int d[N];//保存源点到其他点的最短距离
int map[N][N];//保存两个点之间的距离
bool book[N];//作为某个点是否已经被查找过的标志
int w[N];//保存源点通过最短距离到达其他点时能够聚集的最多的物资
int num[N];//保存源点到其他点的最短距离的数目
const int inf = 1e6;//设定的一个很大的数
int weight[N];//每个点拥有的物资数

void dijkstra(int s,int n)//s为源点(即起始点),一共有n个点
{
	//首先从源点开始搜索,初始时刻源点与其他所有点都未连通(因为未开始查找)
	d[s] = 0;//源点到自身的最短距离为0
	num[s] = 1;//源点到自身的最短路径有一条
	w[s] = weight[s];//源点通过最短路径到自身所能收集到的最多的物资就是源点拥有的物资
	for(int i = 0;i < n; ++i)//查找n次
	{
		//每次查找开始时设定查找的中介点为u
		int u = -1;//初始化u为-1
		int min = inf;//初始化最短距离min为大数inf

		//在未到达的点中,确定一个距离到源点最近的点,作为中介点
		for(int j = 0;j < n;++j)
		{
			if(book[j] == false && d[j] < min)//如果点j未到达(之前未查找过),且j到s距离小于当前min
			{
				u = j;//更新u
				min = d[j];//更新min
			}
		}
		//此时找到了一个中介点u
		book[u] = true;//置点u的标志为true,下次不再使用
		if(u == -1) return;//如果所有的点都到达了(被使用过),直接退出搜索

		//以u为中介,更新s到u再到v的最短距离
		for(int v = 0;v < n;++v)
		{
			if(book[v] == false && map[u][v] != inf)//v未被使用过且点u和v之间连通
			{
				if(d[v] > d[u] + map[u][v])//如果当前保存的v到s的最短距离大于s到u的最短距离+uv的距离
				{//此前提下,s到v的最短路径数目与s到u的相同
					d[v] = d[u] + map[u][v];//更新的d[v]
					num[v] = num[u];
					w[v] = w[u] + weight[v];//更新物资数
				}
				else if(d[v] == d[u] + map[u][v])//找到一条距离相同的路径
				{
					num[v] += num[u];//路径数中需要加上通过u的num[u]条路径
					//多条最短路径时,找出最大的物资数
					w[v] = w[v] > w[u] + weight[v] ? w[v] : w[u] + weight[v];
				}
			}
		} 
	}
}
int main()
{
	int city,line,me,dst;
	cin>>city>>line>>me>>dst;
	fill(d,d + city,inf);//初始赋值大数
	fill(map[0],map[0] + N*N,inf);//初始的map全部赋值大数
	fill(book,book + city,false);
	fill(w,w + city,0);
	fill(num,num + city,0);
	for(int i = 0;i < city;++i)
	{
		cin>>weight[i];
	}
	for(int i = 0;i < line;++i)
	{
		int a,b,l;
		cin>>a>>b>>l;
		map[a][b] = l;//无向边和有向边此处略有不同
		map[b][a] = l;
	}
	dijkstra(me,city);

	cout<<num[dst]<<' '<<w[dst]<<endl;
	return 0;
	
}

1020.Tree Traversals

题目概述:

给出一个二叉树的后序排列和中序排列,求这个二叉树的层序排列。

题目解答:

#include <iostream>
#include <queue>

using namespace std;

int post[30];
int in[30];

struct node
{
	int data;
	struct node* lchild;
	struct node* rchild;
};

struct node* create(int postL,int postR,int inL,int inR)
{
	if(postL > postR)//递归中止的条件,当postL>postR时中止
	{
		return NULL;
	}
	struct node* root = new struct node;//新建一个结点
	root->data = post[postR];//根节点的值为后序排列的最后一个值

	int k;//保存根节点的位置index
	for(k = inL;k <= inR;++k)
	{
		if(in[k] == post[postR])//查找根节点
			break;
	}
	//int numright = postR - k;
	//root->lchild = create(postL,postR - numright - 1,inL,k-1);
	//root->rchild = create(postR - numright,postR - 1,k + 1,inR);
	int numleft = k - inL;//左子树的结点个数
	//左子树的后序排列的起点为preL,终点为preL+(numleft-1)
	//中序排列的起点为inL,终点为k-1
	//右子树的后序排列的起点为preL+(numleft-1) + 1,终点为preR-1
	//中序排列的起点为k+1,终点为inR
	root->lchild = create(postL,postL + numleft - 1,inL,k - 1);
	root->rchild = create(postL + numleft,postR - 1,k + 1,inR);
	return root;
}


int num = 0;
void layerorder(struct node* root,int N)
{
	queue<struct node*> q;//建立队列q,保存结点的地址
	q.push(root);//先压入根节点
	//cout<<root->data;
	while(!q.empty())//当队列非空时
	{
		struct node* now = q.front();
		q.pop();
		cout<<now->data;//访问结点的值
		++num;
		if(num < N)
			cout<<' ';
		//依次压入左子树和右子树的根节点地址
		if(now->lchild != NULL)
			q.push(now->lchild);
		if(now->rchild != NULL)
			q.push(now->rchild);
	}
}


int main()
{
	int N;
	cin>>N;
	for(int i = 0;i < N;++i)
	{
		cin>>post[i];
	}
	for(int i = 0;i < N;++i)
	{
		cin>>in[i];
	}
	struct node* ROOT;
	ROOT = create(0,N-1,0,N-1);
	layerorder(ROOT,N);
	cout<<endl;
	return 0;

}

 

1030.Travel Plan

题目概述:

与1003差不多,每条路径上有一个边权:花费cost。当起点到目的地的最短路径不止一条时,输出最短路径中花费cost最小的路径,以及需要的花费。题目保证最短路径中的最小花费是唯一的。

题目解析:

在1003的dijkstra算法基础上,增加一个全局变量c[N]保存第i个结点到起点的总花费;增加一个全局变量数组pre[n],保存前驱结点的编号,即pre[i]=i的前驱结点。之后通过一个递归函数输出路径。

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

/*
4 5 0 3
0 1 1 20
1 3 2 10
0 3 4 10
0 2 2 20
2 3 1 20
*/
const int N = 500;
const int inf = 1e6;
int d[N];
int map[N][N];
bool book[N];
int costs[N][N];
int c[N];//表示起点到点i的总花费
int num[N];
vector<int> road;
int pre[N];

void dijkstra(int s,int n)
{
	d[s] = 0;
	num[s] = 1;
	c[s] = 0;//起点到自身的总花费为0
	pre[s] = s;//起点的前驱结点是自身
	road.push_back(s);

	for(int i = 0;i < n;++i)
	{
		int u = -1;
		int min = inf;
		for(int j = 0;j < n;++j)
		{
			if(book[j] == false && d[j] < min)
			{
				u = j;
				min = d[j];
			}
		}

		book[u] = true;
		if(u == -1) return;
		
		for(int v = 0;v < n;++v)
		{
			if(book[v] == false && map[u][v] != inf)
			{
				if(d[v] > d[u] + map[u][v])
				{
					d[v] = d[u] + map[u][v];
					num[v] = num[u];
					c[v] = c[u] + costs[u][v];//更新以前驱结点u为中介点时,v到s的总花费
					pre[v] = u;//找到最短路径,此时保存v的前驱结点u

				}

				else if(d[v] == d[u] + map[u][v])
				{
					num[v] += num[u];
					if(c[v] > c[u] + costs[u][v])
					{
						//距离相等的前提下,如果以u为中介点,v到s的总花费小于当前保存的v到s总花费
						//则更新总花费,并且以u为中介点作为新的路径
						c[v] = c[u] + costs[u][v];
						road.push_back(u);
						pre[v] = u;//距离相等的前提下,当花费最小时,更新v的前驱结点u
					}

				}
			}
		}
	}
}

void recur(int s,int now)//s为起点,now以dst终点为输入
{
	if(now == s)//如果递归到了起点,则输出起点并返回函数
	{
		cout<<s;
		return;
	}
	recur(s,pre[now]);//now' = pre[now]
	cout<<' '<<now;//全部递归结束,依次输出now点
}
int main()
{
	int n,m,s,dst;
	cin>>n>>m>>s>>dst;
	fill(d,d + n,inf);
	fill(map[0],map[0] + N*N,inf);
	fill(book,book + n,false);
	fill(costs[0],costs[0] + N*N,inf);
	fill(c,c + n,inf);
	fill(num,num + n,inf);

	for(int i = 0;i < m;++i)
	{
		int a,b,l,cost;
		cin>>a>>b>>l>>cost;
		map[a][b] = l;
		map[b][a] = l;
		costs[a][b] = cost;
		costs[b][a] = cost;
	}
	dijkstra(s,n);

	for(auto it = road.begin();it != road.end(); ++it)
	cout<<*it<<' ';
	cout<<dst<<' '<<d[dst]<<' '<<c[dst]<<endl;
	for(int i = 0;i < n;++i)
	{
		cout<<i<<' '<<pre[i]<<endl;
	}
	recur(s,dst);
	cout<<d[dst]<<' '<<c[dst]<<endl;
	return 0;
}

注意:

代码中有一个road的变量,思路是用一个vector保存路径。这种写法是有问题的,但是如果最终用road输出路径,也能全部通过测试,说明测试点未考虑到一个情况:当出现一条新的路径,长度与当前最短路径相等但是花费却大于原来的最小花费,此时要使用原先的最短路径。但是代码中做不到用road做不到这一点,却同样能通过全部三个测试。这是题目测试点的一个bug。


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值