day7蓝桥(algo1、2、4、5、8)

ALGO-1. 区间k⼤大数查询!


问题描述
给定⼀一个序列列,每次询问序列列中第l个数到第r个数中第K⼤大的数是哪个。
输⼊入格式
第⼀一⾏行行包含⼀一个数n,表示序列长度。
第⼆二⾏行行包含n个正整数,表示给定的序列列。
第三个包含⼀一个正整数m,表示询问个数。
接下来m⾏行行,每⾏行行三个数l,r,K,表示询问序列列从左往右第l个数到第r个数中,从⼤大往⼩小第K⼤大的数是哪
个。序列列元素从1开始标号。
输出格式
总共输出m⾏行行,每⾏行行⼀一个数,表示询问的答案。
样例例输⼊入
5
1 2 3 4 5
2
1 5 2
2 3 2
样例例输出
4
2
数据规模与约定
对于30%的数据,n,m<=100;
对于100%的数据,n,m<=1000;
保证k<=(r-l+1),序列列中的数<=106。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;

bool cmp(int a,int b)
{
	return a>b;
 } 
 int main()
 {
 	int n;
 	cin>>n;
 	vector<int> a(n);
 	int m;
 	int l,r,k;
 	for(int i=0;i<n;i++)
 	{
 		cin>>a[i];
	 }
	 
	 cin>>m; 
	 vector<int> result(m);
	 for(int j=0;j<m;j++)
	 {
	 	cin>>l>>r>>k;//表示询问序列列从左往右第l个数到第r个数中,从?大往?小第K?大的数是哪个。
		int *temp=new int [n];
		for(int j=0;j<n;j++)
			temp[j]=a[j];
		sort(temp+l-1,temp+r,cmp);
		result[j]=temp[l-1+k-1];
		delete []temp;
		
//	 	cout<<v[k-1];
	 }
	 for(int i=0;i<m;i++)
	 	cout<<result[i]<<endl;
	 return 0;
 }

思考:
1、注意动态数组的构建以及手动删除
2、获取的数组的下标

ALGO-2. 最大最小公倍数(贪心算法)!

问题描述
已知⼀一个正整数N,问从1~N中任选出三个数,他们的最⼩小公倍数最⼤大可以为多少。
输⼊入格式
输⼊入⼀一个正整数N。
输出格式
输出⼀一个整数,表示你找到的最⼩小公倍数。
样例例输⼊入
9
样例例输出
504
数据规模与约定
1 <= N <= 10^6。
分析:
1.如果 n <= 2, 那么最⼩小公倍数为 n
2.如果 n 是奇数,那么最⼩小公倍数的最⼤大值为末尾的三个数相乘
3.如果是偶数的话,如果同时出现两个偶数肯定会不不能构成最⼤大值了了,因为会被除以2分两种情
况:
(1)如果 n 是偶数且不不是三的倍数, ⽐比如8,那么跳过n-2这个数⽽而选择 8 7 5 能保证不不会最⼩小公倍数
被除以2~~所以最⼩小公倍数的最⼤大值为n * (n – 1) * (n – 3)
(2)如果 n 是偶数且为三的倍数,⽐比如6,如果还像上⾯面那样选择的话,6和3相差3会被约去⼀一个3,⼜又
不不能构成最⼤大值了了。那么最⼩小公倍数的最⼤大值为(n – 1) * (n – 2) * (n – 3)
代码过程很简单,主要是思路
思考:
1、若n<=2,最小公倍数是n
2、若n为奇数,最小公倍数的最大值是末尾三个数相乘
3、若n为偶数,分为两种情况
(1)n不是3的倍数,例如8,那么跳过n-2,选择8 7 5.能保证最小公倍数不会/2,所以最大值为n*(n-1)*(n-3)
(2)n是3的倍数,例如6,那么要跳过n-2和n-3,选择5 4 3,所以最小公倍数的最大值为(n – 1) * (n – 2) * (n – 3)

#include<iostream>

using namespace std;

int main(){
	
	int n;
	cin>>n;
	if(n<0)
		cout<<"输入不正确";
	else
	{
		if(n<=2) cout<<n;
		else{
			if(n%2==1)
			{
				cout<<(n*(n-1)*(n-2));
			}else{
				if(n%3==0)
					cout<<((n-2)*(n-1)*(n-3));
				else
					cout<<(n*(n-1)*(n-3));
			}
		}
	}
	return 0;
	
}

ALGO-4. 结点选择!


有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果⼀个点被选择了了,那么在树上和它相
邻的点都不能被选择。求选出的点的权值和最大是多少?
第⼀行包含一个整数 n 。
接下来的一行包含 n 个正整数,第 i 个正整数代表点 i 的权值。
接下来⼀共 n-1 行,每行描述树上的一条边。
对于20%的数据, n <= 20。
对于50%的数据, n <= 1000。
对于100%的数据, n <= 100000。
权值均为不超过1000的正整数。
样例输入

5
1 2 3 4 5
1 2
1 3
2 4
2 5

样例输出

12

样例说明
选择3、4、5号点,权值和为 3+4+5 = 12
思考:
题目的意思是第 1 个结点和第 2 个结点相连
第 1 个结点和第 3 个结点相连
第 2 个结点和第 4 个结点相连
第 2 个结点和第 5 个结点相连
所以画出图是这样
在这里插入图片描述
所以看出题目是在算权值的,应该从最深处开始算起,应该用动态规划
1、由题可知树的情况不限,用邻接表进行存储-v[i]数组中保存i结点的孩子节点们,dp[i] [0]表示不取i结点的结果,dp[i][1]表示取i结点的结果
深度优先搜索(追溯到最深层)+动态规划,每个点的最⼤大权值有取当前这个点和不不取当前这个点两种情况~如果取当前点,则不能取与它相邻的任何点;不取当前点,则取与它相邻点的最⼤大值进⾏行行累加~从底向上累加
到顶部~max(dp[1][0], dp[1][1])就是所求结果~

#include<iostream>
#include<vector>

using namespace std;
int dp[10000][2];
vector<vector<int>> v;
void dfs(int son,int far)
{
	for(int i=0;i<v[son].size();i++)
	{
		int temp=v[son][i];
		if(temp!=far)//子节点 
		{
			dfs(temp,son);
			dp[son][1]+=dp[temp][0];
			dp[son][0]+=max(dp[temp][0],dp[temp][1]);
		}
	}
 } 
 
 int main()
 {
 	int n;
 	int a,b;
 	cin>>n;
 	for(int i=1;i<=n;i++)
 	{
 		cin>>dp[i][1];
	 }
	v.resize(n+1);//邻接图 
	 for(int i=1;i<=n-1;i++)
	 {
	 	int a,b;
	 	cin>>a>>b;
	 	v[a].push_back(b);
	 	v[b].push_back(a);
	 }
	 dfs(1,0);
	 cout<<max(dp[1][0],dp[1][1]);
 	return 0;
 }

ALGO-5. 最短路 ?


问题描述
给定⼀一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环)。请你计算从1号点
到其他点的最短路路(顶点从1到n编号)。
输⼊入格式
第⼀一⾏行行两个整数n, m。
接下来的m⾏行行,每⾏行行有三个整数u, v, l,表示u到v有⼀一条⻓长度为l的边。
输出格式
共n-1⾏行行,第i⾏行行表示1号点到i+1号点的最短路路。
样例例输⼊入
3 3
1 2 -1
2 3 -1
3 1 2
样例例输出
-1
-2
数据规模与约定
对于10%的数据,n = 2,m = 2。
对于30%的数据,n <= 5,m <= 10。
对于100%的数据,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保证从任意顶点
都能到达其他所有顶点。

#include<iostream>
#include<vector>

using namespace std;

int main(){
	int n,m;
	cin>>n>>m;
	int u[200001],v[200001],l[20001];
	inf=999999999;
	int dis[20001]={inf,0};
	fill(dis+2,dis+20001,inf);
	for(int i=1;i<=m;i++)
	{
		int u,v,l;
		cin>>u[i]>>v[i]>>l[i];
	}
	for(int i=1;i<=n-1;i++)
	{
		int check=0;
		for(int j=1;j<=m;j++)
		{
			if(dis[v[j]]>dis[u[j]]+l[j])
			{
				dis[v[j]]=dis[u[j]]+l[j];
				check=1;	
			}
		}
		if(check==1) break;
		for(int i=2;i<=n;i++)
			cout<<dis[i]<<endl;
	}
	
	return 0;
}

ALGO-8. 操作格⼦子(线段树)

问题描述
有n个格⼦,从左到右放成⼀排,编号为1-n。
共有m次操作,有3种操作类型:
1.修改⼀个格子的权值,
2.求连续⼀段格子权值和,
3.求连续⼀段格子的最大值。
对于每个2、3操作输出你所求出的结果。
输⼊入格式
第⼀行2个整数n,m。
接下来⼀行n个整数表示n个格子的初始权值。
接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区
间[x,y]内格子权值和,p=3时表示求区间[x,y]内格子最大的权值。
输出格式
有若⼲行,行数等于p=2或3的操作总数。
每行1个整数,对应了每个p=2或3操作的结果。
样例例输入
4 3
1 2 3 4
2 1 3
1 4 3
3 1 4
样例例输出
6
3
数据规模与约定
对于20%的数据n <= 100,m <= 200。
对于50%的数据n <= 5000,m <= 5000。
对于100%的数据1 <= n <= 100000,m <= 100000,0 <= 格⼦子权值 <= 10000。
线段树(线段树是擅长处理区间的)
线段树是一颗完美二叉树(Perfect Binary Tree),树上的每个节点都维护一个区间。根维护的是整个区间,每个节点维护的是父亲的区间二等分后的其中一个子区间。当有n个元素时,对区间的操作可以在O(log n)的时间内完成。
在这里插入图片描述

#include<iostream>
#define max(a,b) a>b?a:b;
using namespace std;

struct node{
	int left,right;
	int maxvalue;
	int sum;
}a[100000];
void init(int left,int right,int i)
{
	a[i].left=left;
	a[i].right=right;
	a[i].maxvalue=0;
	a[i].sum=0;
	if(left!=right)
	{
		int mid=(left+right)/2;
		init(left,mid,2*i);
		init(mid+1,right,2*i+1);
	}
 } //建树
 void insert(int i,int x,int value)
 {//这里的x是序号1-n
 	if(a[i].left==a[i].right)
 	{
 		a[i].maxvalue=value;
 		a[i].sum=value;
 		return;
	 }
	 int mid=(a[i].left+a[i].right)/2;
	 if(x<=mid)
	 	insert(i*2,x,value);
	 else
	 	insert(i*2+1,x,value);
	a[i].maxvalue=max(a[i*2].maxvalue,a[i*2+1].maxvalue);
	a[i].sum=a[i*2].sum+a[i*2+1].sum;	
  } 
  int find_max(int i,int x,int y)
  {
  	if(a[i].left==x&&a[i].right==y)
  		return a[i].maxvalue;
  	int mid=(a[i].left+a[i].right)/2;
  	if(y<=mid)
  		return find_max(i*2,x,y);
  	else if(x>mid)
  		return find_max(i*2+1,x,y);
  	else return max(find_max(i*2,x,mid),find_max(i*2+1,mid+1,y));
  }
  int find_sum(int i,int x,int y)
  {
  	if(a[i].left==x&&a[i].right==y)
  		return a[i].sum;
  	int mid=(a[i].left+a[i].right)/2;
  	if(y<=mid)
  		return find_sum(i*2,x,y);
  	else if(x>mid)
  		return find_sum(i*2+1,x,y);
  	else return find_sum(i*2,x,mid)+find_sum(i*2+1,mid+1,y);
  }
  int main(){
  	int n,m;
  	cin>>n>>m;
  	init(1,n,1);
  	int value;
  	for(int i=1;i<=n;i++)
  	{
  		cin>>value;
  		insert(1,i,value);
	}	
  	for(int j=0;j<m;j++)
	  {
	  	int p,x,y;
	  	cin>>p>>x>>y;
	  	if(p==1)
	  		insert(1,x,y);
	  	if(p==2)
	  		cout<<find_sum(1,x,y)<<endl;
	  	if(p==3)
	  		cout<<find_max(1,x,y)<<endl;
		  }	
	return 0;
  }

本题用了线段树的思路,一般关于区域间的算术用线段树就方便,0(logN),那一列数表明肯定是有规律(从小到大)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值