csp-201903

题目一:小中大【100分】

  1. 时间,内存限制小,数据范围小,简单排序获得最大最小,中位数(处于序列中间位置的数)
  2. 认真读题,注意题目输出要求,中位数是小数保留一位小数,中位数是整数直接输出,并且最大值,最小值一定是整型,直接输出,
    cout<<fixed<<setprecision(1)<<mid <<min,这种控制精度连续输出会导致min也受保留小数位的影响,要分开输出
  3. 整型中位数和小数中位数要分开,
    不能统一定义为int类型中位数-------------无法保留一位小数
    不能统一定义为double类型中位数-------------有精度误差,输出会出错
    问题:
    在C++中,我们会将double、float类型转换成int型,准确的说,将double、float类型取整,会出现以下问题:
    double dTemp1=1.0000;
 
	double dTemp2=5.0000;
 
	int iTemp1=(int)dTemp1; 
 
	int iTemp2=(int)dTemp2;

结果

        iTemp1=0;      //dTemp1是1.00000,但是实际很可能是0.999999,当然截取整数部分,就成了0
 
        iTemp2=4;      //dTemp1是5.00000,但是实际很可能是4.999999,当然截取整数部分,就成了4

如果统一声明double类型中位数,当中位数为整数时cout输出会先将double类型转换为int类型,此时输出就会因为精度误差导致答案错误
AC代码

#include<bits/stdc++.h>
using namespace std;
#define MAXSIZE 10000000
int n;
int num[MAXSIZE];
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>num[i];
	}
	int max,min;     //整型数组,最大最小值一定为整数 
	double mid_d=0;  //分数型中位数
	int mid_i=0;     //整数型中位数
	sort(num,num+n);
	max=num[n-1];
	min=num[0];
	int t=0;
	if(n%2)  //奇数个 
	 mid_i=num[(n-1)/2];
	else 
	{
		int a=num[(n-1)/2];
		int b=num[(n+1)/2];
		t=a+b;
		if(t%2)	mid_d=t/2.0;    //计算过程中有一个为double类型,结果就为double类型
		else mid_i=t/2;
	}
	if(t%2)
	{
		cout<<max<<" "<<fixed<<setprecision(1)<<mid_d<<" ";	
	}
	else
	{
		cout<<max<<" "<<mid_i<<" ";
	}
	cout<<min;
	return 0;
}

其实我们也观察到了,中位数要么是整数,要么就小数位为.5,所以也可以抓住这点,直接对中位数输出进行处理

#include<bits/stdc++.h>
using namespace std;
#define MAXSIZE 10000000
int n;
int num[MAXSIZE];
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>num[i];
	}
	int max,min;
	int mid=0;
	sort(num,num+n);
	max=num[n-1];
	min=num[0];
	int t=0;
	if(n%2)  //奇数个 
	 mid=num[(n-1)/2];
	else 
	{
		int a=num[(n-1)/2];
		int b=num[(n+1)/2];
		t=a+b;
		mid=t/2;
	}
	if(t%2)
	{
		cout<<max<<" "<<mid<<".5 ";	
	}
	else
	{
		cout<<max<<" "<<mid<<" ";
	}
	cout<<min;
	return 0;
}

心得体会

  1. 做题前先读题,看清题目要求,分析数据范围,输入输出要求,时间限制,内存限制
  2. 尽量不要double、int混用,不必要情况不进行他俩的转换,很容易出错,cout输出会默认将小数转化为整数输出

题目五:317号子任务【100分】

这个题我自己挣扎了很久,最多只能95分,实在拿不了100分,
最后参考别人的博客,使用SPFA算法,拿了100
在这里插入图片描述
在这里插入图片描述
真就是一步一步探索了很久,后面我也分析了两篇拿100分的博主的代码,学习了一下,确实很佩服他们

30分: Floyd和基础dijistra、邻接矩阵表示图
60分: 最短的k个排序得到不优化
80分:cin cout
95分:搞不清楚 scanf printf 优化dijistra 优化找最小k个
思路分析

  1. 求取最短路径时只求行星发动机据点到所有点的最短距离;行星发动机据点->普通点距离 =普通点->行星发动机据点距离
  2. 使用邻接链表存图,vector数组
  3. 使用迪杰斯特拉算法,并且使用堆优化,priority
  4. 求前k个的算法需要优化:具体思路是使用一个大根堆,这个堆的最大大小是k个。在堆的size比k小的时候,向堆中push元素;在堆size>=k的时候,比较堆顶值和到当前行星据点距离,如果堆顶值大于到当前行星据点的距离,就将堆顶pop出,push新的到当前行星据点的距离。这样寻找前k个的复杂度是nklogk。

时间复杂度分析

  1. Floyd算法和Dijistra算法求所有顶点相互间的最短距离:n^3
  2. 使用堆优化的Dijistra:n* n *logn
  3. 普通排序查找前k个元素,一般直接用sort:nlogn * k
  4. 堆优化查找前k个元素:nklogk
    这些都是粗略的估计值,具体见另外一篇博客
    四种最短路径算法详细分析

AC代码实现
85分代码,注释、函数都不敢写,写了分数就会变动

#include<bits/stdc++.h>
using namespace std;
#define MAXSIZE 10005
#define INFI 100000000
int n,m,k;
typedef struct Node
{
	int id;
	int W;
}AdjNode,*Adj;
struct cmp{
    bool operator()(const Adj a,const Adj b){
        return a->W > b->W;
    }
};
vector<Adj>List[MAXSIZE];
int sign[MAXSIZE];
int dist[MAXSIZE];
bool visited[MAXSIZE];
priority_queue<int,vector<int>>path[MAXSIZE];
priority_queue<Adj,vector<Adj>,cmp>que;   //按边权值从小到大排序队列 

int main()
{
	//cin>>n>>m>>k;
	scanf("%d %d %d",&n,&m,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&sign[i]);
	}
	for(int i=0;i<m;i++)
	{
		int u,v,w;
		//cin>>u>>v>>w;
		scanf("%d %d %d",&u,&v,&w);
		if(u==v) continue;
		Adj a=new AdjNode;
		a->id=v;
		a->W=w;
		List[u].push_back(a);
		Adj b=new AdjNode;
		b->id=u;
		b->W=w;
		List[v].push_back(b);
	}
	
	for(int i=1;i<=n;i++)
	{
		if(sign[i])
		{
			for(int j=1;j<=n;j++) 
			{
				dist[j]=INFI;
				visited[j]=false;
			}
			dist[i]=0;
			Adj t=new AdjNode;
			t->id=i;
			t->W=0;
			que.push(t);      
			while(!que.empty())
			{
				Adj t_adj=que.top();
				que.pop();   
				if(visited[t_adj->id]) continue; 
				
				int min_i=t_adj->id;
				visited[min_i]=true;
				int len=List[min_i].size();
				for(int j=0;j<len;j++)
				{
					int t=List[min_i][j]->id;
					if(!visited[t]&&dist[min_i]+List[min_i][j]->W<dist[t])
					{
						dist[t]=dist[min_i]+List[min_i][j]->W;
						Adj p=new AdjNode;
						p->id=t;
						p->W=dist[t];
						que.push(p);
					}
				}
			} 
			
			for(int j=1;j<=n;j++)
			{
				if(dist[j]!=INFI) 
				{
					if(path[j].size()<k)	
					path[j].push(dist[j]);
					else
					{
						int t_adj=path[j].top();
						if(t_adj>dist[j]) 
						{
							path[j].pop();
							path[j].push(dist[j]);
						}
 					}
				}
			}
			
		}
	}
	
	for(int i=1;i<=n;i++)
	{
		int sum=0;
		int j=0;
		while(!path[i].empty())
		{
			sum+=path[i].top();
			path[i].pop();
		}
		printf("%d\n",sum);
	}
	return 0;
}

下面这个是相应的解释,代码都是一样的,只不过加了一些注释,交上去分数就不一样

#include<bits/stdc++.h>
using namespace std;
#define MAXSIZE 10005
#define INFI 100000000
int n,m,k;
typedef struct Node
{
	int id;
	int W;
}AdjNode,*Adj;
struct cmp{   //自定义优先级,返回true表示前者优先级更低(与排序刚好相反) 
    bool operator()(const Adj a,const Adj b){
        return a->W > b->W;
    }
};
vector<Adj>List[MAXSIZE];  //vector数组实现邻接表 
int sign[MAXSIZE];
int dist[MAXSIZE];
bool visited[MAXSIZE];
priority_queue<int,vector<int>>path[MAXSIZE];   //最大堆存每个点所能到达的行星据点的距离 
priority_queue<Adj,vector<Adj>,cmp>que;   //按边权值从小到大排序队列 
void Dijistra()   
{
	for(int i=1;i<=n;i++)
	{
		if(sign[i])   //只判断行星据点到其他点的最短距离 
		{
			for(int j=1;j<=n;j++) //初始化 
			{
				dist[j]=INFI;
				visited[j]=false;
			}
			dist[i]=0;
			Adj t=new AdjNode;
			t->id=i;
			t->W=0;
			que.push(t);      //将起点纳入最小堆中,堆顶元素为到当前到起点最近的点 
			while(!que.empty())  
			{
				Adj t_adj=que.top();  //获得堆顶元素(未收纳的顶点距离起点最近的点) 
				que.pop();   
				if(visited[t_adj->id]) continue;  //该点已经收纳到结果集合了,获取下一个 
				
				int min_i=t_adj->id;
				visited[min_i]=true;  //纳入集合 
				int len=List[min_i].size();
				for(int j=0;j<len;j++)   //判断该点受影响的邻接点 
				{
					int t=List[min_i][j]->id;
					if(!visited[t]&&dist[min_i]+List[min_i][j]->W<dist[t])
					{
						dist[t]=dist[min_i]+List[min_i][j]->W;
						Adj p=new AdjNode;
						p->id=t;
						p->W=dist[t];
						que.push(p);   //将离起点距离变短的点纳入最小堆 
					}
				}
			} 
			
			for(int j=1;j<=n;j++)   //求出每个行星据点到其他所有点的最小距离时就将该距离添加到每个行星据点队列后面 
			{                        //并且只存储前k个,多于k个就将最大堆的堆顶元素拿出来 
				if(dist[j]!=INFI) 
				{
					if(path[j].size()<k)	
					path[j].push(dist[j]);
					else
					{
						int t_adj=path[j].top();
						if(t_adj>dist[j]) 
						{
							path[j].pop();
							path[j].push(dist[j]);
						}
 					}
				}
			}
			
		}
	}
}
void Solve()    //输出每个据点队列的行星据点距离并求和 
{
	for(int i=1;i<=n;i++)
	{
		int sum=0;
		int j=0;
		while(!path[i].empty())
		{
			sum+=path[i].top();
		//	cout<<path[i].top()<<" ";
			path[i].pop();
		}
	//	cout<<endl;
	//	cout<<sum<<endl;
		printf("%d\n",sum);
	}
}
int main()
{
	//cin>>n>>m>>k;
	scanf("%d %d %d",&n,&m,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&sign[i]);   //读入据点类型 
	}
	for(int i=0;i<m;i++)
	{
		int u,v,w;
		//cin>>u>>v>>w;
		scanf("%d %d %d",&u,&v,&w);
		if(u==v) continue;
		Adj a=new AdjNode;  //无向图,两个顶点都要进行操作 
		a->id=v;
		a->W=w;
		List[u].push_back(a);
		Adj b=new AdjNode;
		b->id=u;
		b->W=w;
		List[v].push_back(b);
	}
	Dijistra();
	Solve();
	return 0;
}

满分代码
自己最终写的:

#include<bits/stdc++.h>
using namespace std;
#define MAXSIZE 10005
#define INFI 100000000
int n,m,k;
typedef struct Node
{
	int id;
	int W;
}AdjNode,*Adj;
vector<Adj>List[MAXSIZE];  //vector数组实现邻接表 
int sign[MAXSIZE];
int dist[MAXSIZE];
bool inq[MAXSIZE];
priority_queue<int,vector<int>>path[MAXSIZE];   //最大堆存每个点所能到达的行星据点的距离 
queue<int>node;    
void Spfa()   
{
	for(int i=1;i<=n;i++)
	{
		if(sign[i])   //只判断行星据点到其他点的最短距离 
		{
			for(int j=1;j<=n;j++) //初始化 
			{
				dist[j]=INFI;
				inq[j]=false;
			}
			dist[i]=0;
			node.push(i);      //将起点纳入队列中 
			inq[i]=true;
			while(!node.empty())  
			{
				int min_i=node.front();  //得到队列元素并出队 
				node.pop();   
				
				inq[min_i]=false;  //元素出队 
				int len=List[min_i].size();
				for(int j=0;j<len;j++)   //对该点有影响的邻接点都进行松弛 
				{
					int t=List[min_i][j]->id;
					if(dist[t]>dist[min_i]+List[min_i][j]->W)
					{
						dist[t]=dist[min_i]+List[min_i][j]->W;
						if(!inq[t])    //把不在队列里的元素入队 
						{
							node.push(t);
							inq[t]=true;
						}
					}
						
				}
			} 
			
			for(int j=1;j<=n;j++)   //求出每个行星据点到其他所有点的最小距离时就将该距离添加到每个行星据点队列后面 
			{                        //并且只存储前k个,多于k个就将最大堆的堆顶元素拿出来 
				if(dist[j]!=INFI) 
				{
					if(path[j].size()<k)	
					path[j].push(dist[j]);
					else
					{
						int t_adj=path[j].top();
						if(t_adj>dist[j]) 
						{
							path[j].pop();
							path[j].push(dist[j]);
						}
 					}
				}
			}
			
		}
	}
}
void Solve()    //输出每个据点队列的行星据点距离并求和 
{
	for(int i=1;i<=n;i++)
	{
		int sum=0;
		int j=0;
		while(!path[i].empty())
		{
			sum+=path[i].top();
		//	cout<<path[i].top()<<" ";
			path[i].pop();
		}
	//	cout<<endl;
	//	cout<<sum<<endl;
		printf("%d\n",sum);
	}
}
int main()
{
	//cin>>n>>m>>k;
	scanf("%d %d %d",&n,&m,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&sign[i]);   //读入据点类型 
	}
	for(int i=0;i<m;i++)
	{
		int u,v,w;
		//cin>>u>>v>>w;
		scanf("%d %d %d",&u,&v,&w);
		if(u==v) continue;
		Adj a=new AdjNode;  //无向图,两个顶点都要进行操作 
		a->id=v;
		a->W=w;
		List[u].push_back(a);
		Adj b=new AdjNode;
		b->id=u;
		b->W=w;
		List[v].push_back(b);
	}
	Spfa();
	Solve();
	return 0;
}
  1. 参考这篇100分的文章,我的思路跟他的几乎一样
    317任务100分
  2. SPFA算法100分,参考这篇博文
    https://blog.csdn.net/weixin_41297324/article/details/110733559
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值