蓝桥杯高频考点洛谷刷题笔记

蓝桥杯高频考点

P1781 宇宙总统

地球历公元 6036 年,全宇宙准备竞选一个最贤能的人当总统,共有 n 个非凡拔尖的人竞选总统,现在票数已经统计完毕,请你算出谁能够当上总统。

题目传送门

这道题主要难在票数可能达到100位,long long int 不够,如果没有经验的话,不容易想到string 类型的比较。

我们的思路是先比较数字的长度,在比较头一位的大小。要注意string类型得用length()函数获取长度,不能直接使用strlen比较。

n最大只有20,所以对排序速度要求不高。

#include<bits/stdc++.h>
using namespace std;


int main()
{
	int n;
	cin>>n;
	string *a=new string[n+1];
	for(int i=1;i<n+1;i++)
		{
			cin>>a[i];
		}
	string max=a[1];
	int maxpos=1;
	for(int i=2;i<n+1;i++)
	{
		if(a[i].length()>max.length()||(a[i].length()==max.length()&&a[i]>max))
		{
			max=a[i];
			maxpos=i;
		}
	}
	cout<<maxpos<<endl<<max;
		
		
 } 

P3406海底高铁

这道题主要学习了前缀和差分的知识点。
题目传送门

我们做题的时候会发现,如果使用暴力方法去计算经过每一段铁路的次数,那么时间复杂度差不多是O(m*n),如果使用差分,即计算每一段铁路和它前一段铁路被遍历的次数的差值,那么时间复杂度是O(n)。我们使用前缀和的方式,就能得出每一段铁路实际被遍历的次数。

#include<bits/stdc++.h>
using namespace std;

int a[100005],b[100005],c[100005];//Ai,Bi,Ci
long long num[100005];//用于记录经过每一段铁路的次数 
long long int pay(int i)
{
	long long int p1=(long long)a[i]*num[i];
	long long int p2=(long long)c[i]+(long long)b[i]*num[i];
	return min(p1,p2);
}

int main()
{
	int n,m;
	cin>>n>>m;
	int *s=new int[m+1];
	for(int i=1;i<=m;i++)
		cin>>s[i]; 
	 
	for(int i=1;i<m;i++)//差分 
	{
	 num[max(s[i],s[i+1])]--;
	 num[min(s[i],s[i+1])]++;
	}		
	long long int sum=0;
	for(int i=1;i<n;i++)
	{
		cin>>a[i]>>b[i]>>c[i];
		num[i]+=num[i-1];
		sum+=pay(i);
	}
	cout<<sum;
}

还有一点要特别注意,就是long long的使用。int*int得到的结果还是int,如果要不出错,在前面得加上(long long)强制类型转换。

P7072 直播获奖

题目传送门

我们一开始直接使用sort,得分只有65.后来发现可以使用桶排序的思想来解题。因为分数只有601种可能,所以我们可以把每个分数都看作一个桶。
具体实现如下

#include<bits/stdc++.h>
using namespace std;

bool cmp(int a,int b)
{
	return a>b;
}
int main()
{
	int n,w;
	int tong[601]={};//借鉴桶排序的思想,要记得初始化 
	cin>>n>>w;
	int *s=new int[n+1];
	int pos=1; 
	for(int i=1;i<=n;i++)
	{
		cin>>s[i];
		tong[s[i]]++; 
		pos=max(1,i*w/100); 
		int sum=0;
		for(int j=600;j>=0;j--)
		{
			sum=sum+tong[j];
			if(sum>=pos)
			{
				cout<<j<<" ";
				break;
			}
		}
	}
	return 0;
}

P1115 最大子段和

题目传送门

这道题我一开始卡了挺久,没啥思路。后来发现我们只要要考虑每个a[i]是否需要加上前面的子段和就行。所以我们用sum来记录前面的子段和的大小,显然,如果sum<0,我们就不需要加上前面的子段了,这时可以令sum=0,然后再加上当前a[i]。我第一次这么做的,然后只有80分。问题出在哪呢。想想看哈,如果所有ai都小于0,那最终会输出0,这显然不是我们想要的答案。

我们在这过程中用一个变量record来记录最大子段和的大小,将其初始化为a[0]。我们将sum也初始化为a[0],在每一轮循环中,当前的sum需要和0做比较,如果当前sum小于0,record=max(record,a[i]),否则sum=sum+a[i]。每一轮循环结束以后,要使用record=max(sum,record)来记录实时的最大子段和。

#include<bits/stdc++.h>
using namespace std;

int main()
{
	int n;
	cin>>n;
	int *a=new int [n];
	for(int i=0;i<n;i++)
		cin>>a[i];
	int record=a[0]; 
	int sum=a[0];
	for(int i=1;i<n;i++)
	{
		if(sum<0)
		{
			record=max(record,a[i]);
			sum=a[i];
			continue;
		}
		else
			sum=sum+a[i];
		record=max(sum,record);
	}
	cout<<record;
}

P1048 采药

题目传送门

这道题考察01背包问题。关键步骤是,当前这株草药的采摘时间如果小于等于允许的总时间,那么我们需要考虑要不要采摘这株草药。这个时候就要比较当前草药的价值加上减去采摘这株草药时间的最大草药价值同一时间下,不考虑采摘这株草药的最大价值

#include<bits/stdc++.h>
using namespace std;

struct caoyao{
	int t;//采摘时间
	int v;//草药价值 
};

int main()
{
	int T,M;
	cin>>T>>M;
	caoyao a[105];
	int dp[105][1005]={};
	for(int i=1;i<=M;i++)
		cin>>a[i].t>>a[i].v;
	for(int i=1;i<=M;i++)
	{
		for(int j=1;j<=T;j++)
		{
			if(j<a[i].t)
			{
				dp[i][j]=dp[i-1][j];
			}
			else
			{
				int data1=dp[i-1][j];
				int data2=dp[i-1][j-a[i].t]+a[i].v;
				dp[i][j]=max(data1,data2);
			}
				
		}

	}
	cout<<dp[M][T];
}

P1616 疯狂地采药

题目传送门
这道题用到了完全背包问题,但是由于数据量比较大,我们使用二维数组肯定会导致空间溢出,所以我们换用一维滚动数组。

奇怪的是,我在DEVC++上调试10e6空间大小的一维数组就溢出了,但是使用洛谷的在线环境是能通过的。还有就是,dp数组得设置为全局变量,否则也会溢出,实在是奇怪。

以下是来自deepseek的解释:
dp数组作为局部变量在栈上申请,而dp[10000005]占用约76MB内存(long long类型),远超栈空间限制(通常约几MB)。

#include<bits/stdc++.h>
using namespace std;
#define in long long
struct caoyao{
	in w;
	in v;
};
in dp[10000005]={};
int main()
{
	caoyao a[10005];
	in t,m;
	cin>>t>>m;
	for(in i=1;i<=m;i++)
		cin>>a[i].w>>a[i].v;
	for(in i=1;i<=m;i++)
		for(in j=a[i].w;j<=t;j++)
		{
			dp[j]=max(dp[j],dp[j-a[i].w]+a[i].v);
		}
	cout<<dp[t];

 } 

P1090合并果子

题目传送门

贪心。
怎么有人调试了半天然后忘记排序了啊,天啊。

#include<bits/stdc++.h>
using namespace std;
bool cmp(long long a,long long b)
{
   return a<b;
}

int main()
{
   int n;
   cin>>n;
   long long a[10005];
   for(int i=0;i<n;i++)
   	cin>>a[i];
   sort(a,a+n,cmp);
   long long spend=0;
   long long smin=a[0];//当前最小堆
   for(int i=1;i<n;i++)
   {
   	smin=smin+a[i];
   	spend+=smin;
   	long long t=a[i+1];
   	if(t<smin)
   	{
   		for(int j=i+1;j<n;j++)
   		{
   			if(j<n-1)
   				a[j]=a[j+1];
   			if(a[j+1]>=smin||j==n-1)
   			{
   				a[j]=smin;
   				smin=t;
   				break;
   			}
   		}	
   	}
    } 
    cout<<spend;
}

这道题还可以使用优先队列。(优先队列默认从大到小排列)

以下这段代码使出队列元素从小到大排列。

priority_queue<long long,vector<long long>,greater<long long>> que;

完整代码如下。

#include<bits/stdc++.h>
using namespace std;

int main()
{
	priority_queue<long long,vector<long long>,greater<long long>> que;
	int n;
	cin>>n;
	long long a;
	for(int i=0;i<n;i++)
	{
		cin>>a;
		que.push(a);
	}
	long long sum=0;
	long long smin;//用于记录当前最小堆 
	for(int i=0;i<n-1;i++)
	{
		long long a=que.top();
		que.pop();
		long long b=que.top();
		que.pop();
		smin=a+b;
		sum+=smin;
		que.push(smin);
	}
	cout<<sum;	
 } 

P1536 村村通

题目传送门

并查集。

#include<bits/stdc++.h>
using namespace std;


int find(int rt,int fa[])
{
	if(rt==fa[rt]) return rt;
	else return fa[rt]=find(fa[rt],fa);
}
int main()
{
	int n,m;
	cin>>n>>m;
	while(n!=0)
	{
		int fa[1002];
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		int a,b;
		cin>>a>>b;
		if(a>b)//得按照一定的大小顺序
		{
			int t=a;
			a=b;
			b=t;
		}
		int rt=find(fa[b],fa); 
		fa[rt]=find(fa[a],fa);//将b的根设为a的根 
	}
	int sum=0;//用于记录还缺多少路 
	for(int i=1;i<n;i++)
	{
		if(find(i,fa)!=find(i+1,fa))
		{
			int rt=find(fa[i+1],fa);
			fa[rt]=find(fa[i],fa);//将i+1的根设为i的根 
			sum++;
		}
	}
	cout<<sum<<endl;
	cin>>n;
	if(n==0)
		return 0;
	else
		cin>>m;
	}
	return 0;
}

P1141 01迷宫

题目传送门

bfs

#include<bits/stdc++.h>
using namespace std;

int nmap[1002][1002];
int memory[1002][1002];
bool vis[1002][1002];
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
struct pairs{	//用于表示每个格子位置的结构体 
	int x;
	int y;
}; 
int n,m;

void bfs(int i,int j)
{
	int cnt=1;//用于统计能移动到的格子数
	pairs s1;
	s1.x=i;
	s1.y=j;
	queue<pairs> q;
	queue<pairs>mq;
	q.push(s1);
	mq.push(s1);
	vis[i][j]=true;
	while(q.size())
	{
		pairs s=q.front();
		for(int t=0;t<4;t++)
		{
			if(nmap[s.x+dx[t]][s.y+dy[t]]!=nmap[s.x][s.y]&&memory[s.x+dx[t]][s.y+dy[t]]!=0)
			{
				cnt=memory[s.x+dx[t]][s.y+dy[t]];
				cout<<cnt<<endl;
				while(mq.size())
				{
					pairs s3=mq.front();
					memory[s3.x][s3.y]=cnt;
					mq.pop();
				}
				return ;
			}
			if(nmap[s.x+dx[t]][s.y+dy[t]]==nmap[s.x][s.y]||vis[s.x+dx[t]][s.y+dy[t]]||s.x+dx[t]<1
			||s.x+dx[t]>n||s.y+dy[t]<1||s.y+dy[t]>n)
			{
				continue;
			}
			pairs s2;
			s2.x=s.x+dx[t];
			s2.y=s.y+dy[t];
			vis[s.x+dx[t]][s.y+dy[t]]=true;
			q.push(s2);
			mq.push(s2);
			cnt++;
		}
		q.pop();
	}
	while(mq.size())
	{
		pairs s3=mq.front();
		memory[s3.x][s3.y]=cnt;
		mq.pop();
	}
	cout<<cnt<<endl;
 } 
 
 int main()
 {
 	cin>>n>>m;
 	memset(memory,0,sizeof memory);
 	for(int i=1;i<=n;i++)
 		for(int j=1;j<=n;j++)
 			{
 				char ch;
 				cin>>ch;
 				nmap[i][j]=ch-'0';
			 }
 	
 	int *sx=new int[m];
 	int *sy=new int[m];
 	for(int i=0;i<m;i++)
 		cin>>sx[i]>>sy[i];
 	for(int i=0;i<m;i++)
 	{
 		bfs(sx[i],sy[i]);
	 }
 	return 0;
 }

P8782 X进制减法

题目传送门

实在是太不容易了。这道题,一开始是题目不好理解,理解完题目会发现,算法的整体思路并不难。于是我很快乐地敲完了代码,一运行,30分。坑实在太多了。
首先是数据的大小问题,由于ma,mb都可能达到10e5的量级,也就是说,a和b两个数可能特别特别大,每一位的权值也特别特别大,没有任何类型的变量能够装得下。所以我们必须边走边模。这是第一个坑。这个坑爬过去之后,应该能有80分了。

为啥是80分呢,嗯,忘了一个情况,a和b可能都是负数,mb也可能大于ma。

好了,跳过所有坑之后,AC代码如下。

#include<bits/stdc++.h>
using namespace std;
int n;
int a[100005];
int b[100005];
int mod=1e9+7;
int select(int a,int b)//选择进制的函数 
{
	if(a>=b)
		return max(a+1,2);
	else
		return max(b+1,2);
	
}

int main()
{
	cin>>n;
	for(int i=0;i<=100005;i++)//先全部初始化为0 
	{
		a[i]=0;
		b[i]=0;
	}
	int ma;
	cin>>ma;
	for(int i=ma;i>0;i--)
		cin>>a[i];
	int mb;
	cin>>mb;
	for(int i=mb;i>0;i--)
		cin>>b[i];
	int m=max(ma,mb);//不知道哪个更大,一开始没考虑到负数
	long long t=1;//用于表示当前位置的权值 
	long long sum=0;//用于计算总差值 
	for(int i=1;i<=m;i++)
	{
		sum=(sum+(a[i]-b[i])*t%mod)%mod;
		//cout<<sum<<endl;
		t=t*select(a[i],b[i])%mod; 
	}
	cout<<(sum+mod)%mod<<endl;
	return 0;
}

P1551 亲戚

题目传送门

红红火火恍恍惚惚并查集秒了

#include<bits/stdc++.h>
using namespace std;

int fa[5002];

int f(int a)
{
	if(fa[a]==a)
		return a;
	else
		return fa[a]=f(fa[a]);
}

int main()
{
	int n,m,p;
	cin>>n>>m>>p;
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		int a,b;
		cin>>a>>b;
		fa[f(a)]=f(b);
	}
	for(int i=1;i<=p;i++)
	{
		int a,b;
		cin>>a>>b;
		if(f(a)==f(b))
			cout<<"Yes"<<endl;
		else
			cout<<"No"<<endl;
	}
	return 0;
}

P1443 马的遍历

题目传送门

bfs的题目,自己做完,再看了看别人的题解,发现自己好像想复杂了,此题可以不用新设置一个num数组来统计步数,直接用地图上此时正在遍历的点的父节点的值+1就可以。

#include<bits/stdc++.h>
using namespace std;

int nmap[405][405];
bool vis[405][405];
int num[160005];
int dx[8]={1,1,2,2,-1,-1,-2,-2};
int dy[8]={2,-2,1,-1,2,-2,1,-1};
struct pairs{
	int x;
	int y;
};
int n,m;
void bfs(int x1,int y1)
{
	pairs s;
	s.x=x1;
	s.y=y1;
	int cnt=0;
	nmap[s.x][s.y]=0;
	vis[x1][y1]=true;
	queue<pairs> q;
	q.push(s);
	num[0]=1;
	while(q.size())
	{
		pairs s2;
		s2=q.front();
		int x,y;
		x=s2.x;y=s2.y;
		if(num[cnt]==0) cnt++;
		for(int i=0;i<8;i++)
		{
			if(vis[x+dx[i]][y+dy[i]]||x+dx[i]<1||x+dx[i]>n||y+dy[i]<1
			||y+dy[i]>m)
				continue;
			else
			{
				vis[x+dx[i]][y+dy[i]]=true;
				nmap[x+dx[i]][y+dy[i]]=cnt+1;
				pairs s1;
				s1.x=x+dx[i];
				s1.y=y+dy[i];
				q.push(s1);
				num[cnt+1]++;
				//cout<<"num[cnt]:"<<num[cnt]<<"  cnt:"<<cnt<<endl;
			}
		}	
		q.pop();
		num[cnt]--;
	}
}

int main()
{
	int x,y;
	cin>>n>>m>>x>>y;
	memset(nmap,-1,sizeof nmap);
	bfs(x,y);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
			cout<<nmap[i][j]<<" ";
		cout<<endl;
	}	
	
}

P4387 验证栈序列

题目传送门
怎么有人用了真的栈,还写得这么复杂。不过洛谷这道题的数据可能不是很强,我总感觉我写的有漏洞。

#include<bits/stdc++.h>
using namespace std;

int main()
{
	int q;
	cin>>q;
	while(q!=0)
	{
		q--;
		int n;
		cin>>n;
		stack<int> s;
		queue<int> pushed;
		queue<int> poped;
		for(int i=0;i<n;i++)
		{
			int a;
			cin>>a;
			pushed.push(a);
		}
		for(int i=0;i<n;i++)
		{
			int a;
			cin>>a;
			poped.push(a);
		}
		while(pushed.size())
		{
			if(pushed.front()!=poped.front())
			{
				if(s.size()&&poped.front()==s.top())
					{
						s.pop();
						poped.pop();
						continue;
					}
				else
					{
						s.push(pushed.front());
						pushed.pop(); 
					}
			}
			else
			{
				pushed.pop();
				poped.pop();
			}
		}
		bool flag=true;
		while(poped.size())
		{
			if(s.size()&&poped.front()==s.top())
			{
				poped.pop();
				s.pop();
			}
			else
			{
				flag=false;
				break;
			}
		}
		if(flag==false)
			cout<<"No"<<endl;
		else
			cout<<"Yes"<<endl;
	}
	return 0;
}

P1160队列安排

题目传送门

这道题很明显的链表题。
首先是使用STL容器的解法。对于不太熟悉该容器的我,这个解法其实反而有点麻烦。有很多函数我都不太清楚怎么用。

#include<bits/stdc++.h>
using namespace std;
bool remov[100002];
list<int>::iterator pos[100002];//这一行要学会 
int main()
{
	list<int> list1;
	int n;
	cin>>n;
	list1.push_back(1);
	pos[1] = list1.begin();
	for(int i=2;i<=n;i++)
	{
		int k,p;
		cin>>k>>p;
		if(p==0)
		{
			pos[i]=list1.insert(pos[k],i);//这里标记,就不用再使用find函数,find函数会导致超时 
		}
		else pos[i]=list1.insert(next(pos[k]),i);
	}
	int m;
	cin>>m;
	for(int i=1;i<=m;i++)
	{
		int a;
		cin>>a;
		if(!remov[a])	//主要是一个元素只能删一次,删第二次会导致出错,所以需要remov函数来标记一下 
		{
			list1.erase(pos[a]);
			remov[a]=true;
		}
			
	}
	 for (auto it = list1.begin(); it != list1.end(); ++it)
    {
        cout << *it << " ";
    }
    return 0;
}

我参考了大佬们的题解,发现模拟链表写起来可能还容易些。但是在增添节点的时候,双向链表要尤其注意,左节点和右节点都得改变。

#include<bits/stdc++.h>
using namespace std;
struct student{
	int l=0;
	int r=0;
	bool d=false;//用于表示是否删除 
};

int main()
{
	int n;
	cin>>n;
	student *s=new student[n+1];
	for(int i=2;i<=n;i++)
	{
		int k,p;
		cin>>k>>p;
		if(p==0)
		{
			int t=s[k].l;
			s[k].l=i;
			s[i].l=t;
			s[i].r=k;
			s[t].r=i;
		}
		else
		{
			int t=s[k].r;
			s[k].r=i;
			s[i].r=t;
			s[i].l=k;
			s[t].l=i;
		}
	}
	int m;
	cin>>m;
	for(int i=1;i<=m;i++)
	{
		int x;
		cin>>x;
		s[x].d=true;
	}
	int now;
	for(int i=1;i<=n;i++)
	{
		if(s[i].l==0)
		{
			now=i;
			break;
		}
	}
	cout<<now<<" ";
	while(s[now].r!=0)
	{
		now=s[now].r;
		if(!s[now].d)
			cout<<now<<" ";
	}
	return 0;
 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值