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),那一列数表明肯定是有规律(从小到大)