PTA天梯练习赛 刷题笔记(L2 001-004)(更新中)

题目库链接:团体程序设计天梯赛-练习集

目录

OP

\

L2-001 紧急救援 (25 分)

dijkstra(迪杰斯特拉)算法
大佬说SPFA也可以做,mark一下,接下来学

思路

可以参考这里来了解迪杰斯特拉的基本原理;

此题的主要特定是最短路可能有多种规划路线,而最优解只有一个,即需要同时处理距离和人数两个维度的信息;

主要思想是将所有点分为已达点与未达点两个集合(代码中用一个数组进行01标记),最开始时,已达点集合仅有出发点,未达点集合包含其余所有点;

主要过程是每一次循环遍历所有未达点,对于每一个未达点 pj ,如果可以通过一条道路(长度 ki )连接至已达点 pi ,则记录由此未达点到达初始点的最短距离(设 pi 点到起始点的最短距离 li, l j = m i n ( l i + k i ) l_j=min(l_i+k_i) lj​=min(li​+ki​)),并记录所有能使 lj 取最小值的 i ,作为到达此点的方案数;

在所有未达点中选择距离初始点最短的点,将其加入已达点集合,并在所有可行方案( i )中选择积累人数最多的点作为最终与其连接的点,新点的累计人数即为与其连接点的累积人数和新点原有人数的和;

如此循环,直到目标点已达;

最后按顺序找出整个路径上的各个点并输出即可。

代码注释已加
进行人数存储的部分可优化,即smp变量不必要
时间复杂度O(n3)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=1e9+7;
struct 
{
	vector<int>fr;//存储其上游的点,即from i
	int strd=100000000;//存储距离初始点的距离,默认无穷大
	int p=0;//存储此点的人数
	int smp=0;//存储从初始点到此点的累计人数
}cty[502];

int main()
{
	int n,m,f,t,a,b,l;//n为城市数,m为道路书,abl为接收道路用变量
	int r[502][502]={0},rch[502]={0};//r即road,存储道路,长度为0即无路,此处可优化
									//rch数组存储已达点及方案数,0为未达
	cin>>n>>m>>f>>t;
	cty[f].fr.push_back(f);//初始化处理开始点
	rch[f]=1;
	cty[f].strd=0;
	for(int i=0;i<n;i++)
	{
		cin>>cty[i].p;
		cty[i].smp=cty[i].p;
	}//接收城市救援人数
	for(int i=0;i<m;i++)//接收路,对称加入存储
	{
		cin>>a>>b>>l;
		if(r[a][b])r[a][b]=min(r[a][b],l);
		else r[a][b]=l;
		if(r[b][a])r[b][a]=min(r[b][a],l);
		else r[b][a]=l;
	}
	while(!rch[t])//如果目标点未达,则持续循环
	{
		int mnr=100000000-8,mnm=-1;//mnr存储未达点距离初始点的距离最小值
									//mnm存储满足mnr点的编号
		for(int i=0;i<n;i++)
		{
			if(!rch[i])//如果未达
			{
				cty[i].fr.clear();//初始化
				cty[i].strd=100000000;
				for(int j=0;j<n;j++)
				{
					if(rch[j]&&r[i][j])//遍历每一条通往可达点的道路
					{
						if(cty[j].strd+r[i][j]<cty[i].strd)//更小值
						{
							cty[i].strd=cty[j].strd+r[i][j];//更新最小值
							cty[i].fr.clear();//情况方案
							cty[i].fr.push_back(j);//j加入方案
						}
						else if(cty[j].strd+r[i][j]==cty[i].strd)//与最小值相等的值
						{
							cty[i].fr.push_back(j);//j加入方案
						}
					}
				}
				if(cty[i].strd<mnr)//更新此次循环的最小距离
				{
					mnr=cty[i].strd;
					mnm=i;
				}
			}
		}
		int mxp=0,mxm=-1;//mxp记录最多积累人数,mxm记录mxp对应的城市编号
		for(int j:cty[mnm].fr)
		{
			rch[mnm]+=rch[j];//到达新点的最终方案数为每一个可行方案对应的点方案数之和
			if(cty[j].smp>mxp)//更新
			{
				mxp=cty[j].smp;
				mxm=j;
			}
		}
		cty[mnm].fr.clear();//清空方案
		cty[mnm].fr.push_back(mxm);//并将积累人数最多的方案定为最终方案
		cty[mnm].smp+=cty[mxm].smp;//更新积累人数
		//printf("%d %d\n",mnm,rch[mnm]);
	}
	printf("%d %d\n",rch[t],cty[t].smp);//输出方案数和最优方案人数
	stack<int>stk;//栈存储路径结点
	int now=t;
	stk.push(t);
	stk.push(cty[now].fr[0]);
	now=cty[now].fr[0];
	//printf("*%d",stk.top());
	while(stk.top()!=f)
	{
		stk.push(cty[now].fr[0]);
		now=cty[now].fr[0];
		//printf("*%d",stk.top());
	}
	printf("%d",stk.top());//输出开始
	stk.pop();
	while(stk.size())
	{
		printf(" %d",stk.top());
		stk.pop();
	}
    return 0;
}

L2-002 链表去重 (25 分)

数据结构

思路

存住上一个节点,按要求更新处理即可;

要握住两个链表的起始点;

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=1e9+7;
struct 
{
	int num;
	int nxt;
}vtr[100005];

int main()
{
	int f1,n,g,f2=-1;
	cin>>f1>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>g;
		cin>>vtr[g].num>>vtr[g].nxt;
	}//接收结束
	int now=f1,a[10004]={0},lst=0,now2;//a用来桶去重,now为正在处理的节点,lst为上一个节点,now2为另一个链表的末尾
	while(now!=-1)//未到f1的结尾则继续循环
	{
		if(a[abs(vtr[now].num)])
		{
			vtr[lst].nxt=vtr[now].nxt;
			if(f2==-1)f2=now,now2=now,now=vtr[now].nxt,vtr[now2].nxt=-1;//若另一链表未被建立
			else
			{
				vtr[now2].nxt=now;
				now2=now;
				now=vtr[now].nxt;
				vtr[now2].nxt=-1;
			}
			
		}
		else
		{
			a[abs(vtr[now].num)]=1;
			lst=now;
			now=vtr[now].nxt;
		}
	}
	now=f1;
	while(now!=-1)
	{
		printf("%05d %d ",now,vtr[now].num);
        if(vtr[now].nxt==-1)printf("%d\n",-1);
        else printf("%05d\n",vtr[now].nxt);//注意要求保留位数
		now=vtr[now].nxt;
	}
	now=f2;
	while(now!=-1)
	{
		printf("%05d %d ",now,vtr[now].num);
        if(vtr[now].nxt==-1)printf("%d\n",-1);
        else printf("%05d\n",vtr[now].nxt);
		now=vtr[now].nxt;
	}
    return 0;
}

L2-003 月饼 (25 分)

模拟,贪心

思路

模拟即可,优先卖单价高的;

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=1e9+7;
struct mooncake
{
	double w;
	double m;
}mnk[1003];

bool cmp(const mooncake a,const mooncake b)
{
	return 1.0*a.m/a.w>1.0*b.m/b.w;
}
//money per weight 按单价降序排序
int main()
{
	int t;
    double n;
	cin>>t>>n;
	for(int i=1;i<=t;i++)cin>>mnk[i].w;
	for(int i=1;i<=t;i++)cin>>mnk[i].m;
	sort(mnk+1,mnk+t+1,cmp);
	double s=0;
	for(int i=1;i<=t&&n;i++)
	{
		if(n>=mnk[i].w)
		{
			n-=mnk[i].w;
			s+=mnk[i].m;
		}
		else
		{
			s+=1.0*n*mnk[i].m/mnk[i].w;
			n=0;
		}
	}
	printf("%.2lf",s);
    return 0;
}

L2-004 这是二叉搜索树吗? (25 分)

树的前序遍历

思路

对于题目中描述的二叉搜索树,满足以下性质:
对于一棵树前序遍历 [ l , r ] ,第一个元素必为根节点;
设从第二个节点开始,第一个大于等于根节点的节点编号为mk, m a x [ l , m k − 1 ] < r o o t , m i n [ m k , r ] < = r o o t max [ l , mk - 1 ]<root,min[ mk , r ]<=root max[l,mk−1]<root,min[mk,r]<=root;

镜像树类似;

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=1e9+7;
queue<int>que;
int a[1003];
bool ju(int l,int r,int dir)//dir=1即为正向树,dir=0即为镜像树
{
	int i,root=a[l],ans=1,mk=r+1,mi=root,mx=root-1;
	//root存储子树根节点的值,mk为右子树的根节点
	//mi为目标子树的最小值,mx为目标字数的最大值
	/*if(l==r)			//防呆代码,实际上用不到
	{
		que.push(a[l]);
		return 1;
	}
	else if(l>r)
	{
		return 1;
	}*/
	if(dir)
	{
		for(i=l+1;i<=r;i++)
		{
			if(a[i]>=root)mk=min(mk,i);
			if(mk==r+1)mx=max(mx,a[i]);//mk==r+1说明此处仍为左子树
			else mi=min(mi,a[i]);
		}
		if(mk!=l+1&&mx>=root||mk!=r+1&&mi<root)//对于mk的判据可以删除
		{
			//printf("%d %d %d %d %d\n",l,r,mi,mx,mk);
			return 0;
		}
	}
	else
	{
		for(i=l+1;i<=r;i++)
		{
			if(a[i]<root)mk=min(mk,i);
			if(mk==r+1)mi=min(mi,a[i]);
			else mx=max(mx,a[i]);
		}
		if(mk!=r+1&&mx>=root||mk!=l+1&&mi<root)
		{
			//printf("%d %d %d %d %d\n",l,r,mi,mx,mk);
			return 0;
		}
	}
	if(mk!=l+1)//若左子树存在
	{
		ans*=ju(l+1,mk-1,dir);
	}
	if(mk!=r+1)//若右子树存在
	{
		ans*=ju(mk,r,dir);
	}
	que.push(root);//根节点入栈,栈内即为后序遍历
	return ans;
}
int main()
{
	int n,i,f=1;
	cin>>n;
	for(i=1;i<=n&&f;i++)cin>>a[i];
	{
		if(a[1]>a[2])f=ju(1,n,1);//正向树
		else f=ju(1,n,0);//镜像树
		if(f)
		{
			printf("YES\n",que.size());
			while(!que.empty())
			{
				printf("%d",que.front());
				if(que.size()!=1)printf(" ");
				que.pop();
			}
		}
		else printf("NO");
	}
    return 0;
}
上一篇:001 课程定位和目标


下一篇:怎么用 hammerspoon 来进行窗口管理