蓝桥杯试题-算法篇1

问题描述

给定一个序列,每次询问序列中第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

解答:

先用冒泡排序法把整个数组从大到小排序,同时保存各个数在原数组的位置。最后每次寻找第k个大的数时,只需要遍历一次location,每发现一个位置在指定范围内,则计数器加1,直到计数器的值为k时,则说明找到了第k大的数。

#include
using namespace std;

int main() {
	int n;
	int a[1001];
	int m;
	int q[1000][3];
	cin>>n;
	int location[1001];
	for(int i=1;i<=n;i++){
		cin>>a[i];
		location[i]=i; 
	}
	cin>>m;
	for(int i=0;i>q[i][0]>>q[i][1]>>q[i][2];
	}
	
	//排序
	for(int i=1;ii;j--){
			if(a[j]>a[j-1]){
				int temp=a[j];
				a[j]=a[j-1];
				a[j-1]=temp;
				//同时位置交换
				temp=location[j];
				location[j]= location[j-1];
				location[j-1]=temp;
			}
		}
	}
	for(int i=0;i=q[i][0]&&location[j]<=q[i][1]) count++;
			if(count==q[i][2]){
				cout<

问题描述

已知一个正整数N,问从1~N中任选出三个数,他们的最小公倍数最大可以为多少。

输入格式

输入一个正整数N。

输出格式
输出一个整数,表示你找到的最小公倍数。
样例输入
9
样例输出
504
数据规模与约定

1 <= N <= 106



解答:
按照直观的思路我们可以这么做:先写一个求3个数的最小公倍数的函数LCM。然后三重循环找出所有可能的三个数的组合,每个组合求一次最小公倍数,然后取最大的数即可。

         在我的印象中,最小公倍数的求法可以用一个循环从任意一个数开始到三个数的乘积找到一个最小的数,这个数分别对三个数取模都为0;然而可能事情没那么简单,既然这倒题的考点为贪心算法,那么这么做十之八九可能会超时,不管怎么样先用普通的方法来算一下,然后对比一下参考答案来看看差距好了。

#include
using namespace std;

int LCM(int a,int b,int c){
	for(int i=a;i<=a*b*c;i++){
		if(i%a==0 &&i%b==0 &&i%c==0) return i;
	}
} 
int main() {
	long long int n;
	cin>>n;
	long long int max=0;
	for(long long int i=1;i<=n;i++){
		for(long long int j=1;j<=n;j++){
			for(long long int k=1;k<=n;k++){
				if(i==j && j==k)continue;
				long long int lcm=LCM(i,j,k);
				if(lcm>max) max=lcm;
			}
		}
	}
	cout<

        结果蓝桥杯测试的时候第一个数据就是万级别的,最后当然超时了,虽然这段代码在当n很小时并不会有问题,但是当n比较大时,就基本没用了。以前都没有接触过贪心算法,现在来看看参考答案吧。

#include  
using namespace std;  
int main()  
{  
    long long n,ans;  
    cin>>n;  
    if(n<=2)  
    ans=n;  
    else if(n%2==1)  
    ans=n*(n-1)*(n-2);  
    else  
    {  
        if(n%3==0)  
        ans=(n-1)*(n-2)*(n-3);  
        else  
        ans=n*(n-1)*(n-3);  
    }  
    cout<

引用这位博主的解释:http://blog.csdn.net/wr132/article/details/43538151。既然是贪心,那么肯定怎么贪怎么做,实在不行就退而求其次。所以在本题中最贪的情况是什么?那就是n*n*n。但是很明显这不符合最小公倍数的要求,同理n*n*(n-1)也不行,那么看看n*(n-1)*(n-1)。根据互质的性质:

1.相邻的两个自然数互质 

2.相邻的两个奇数互质 

因此,当n为奇数时,三个数分别为奇偶奇,即两两互质,不存在公约数,因此n*(n-1)*(n-2)就为最小公倍数了。

当n为偶数时,三个数分别为奇偶,那么肯定存在公约数2,即n*(n-1)*(n-2)/2就能够整除三个数了,这样数就直接小了0.5倍。再次退而求其次看看n*(n-1)*(n-3)行不行。这时三个数分别为奇奇,因为n-(n-3)=3,则当n为3的倍数时,存在因子3,所以再退而求其次(n-1)*(n-2)*(n-3)。


问题描述

如果一个自然数N的K进制表示中任意的相邻的两位都不是相邻的数字,那么我们就说这个数是K好数。求L位K进制数中K好数的数目。例如K = 4,L = 2的时候,所有K好数为11、13、20、22、30、31、33 共7个。由于这个数目很大,请你输出它对1000000007取模后的值。

输入格式

输入包含两个正整数,K和L。

输出格式
输出一个整数,表示答案对1000000007取模后的值。
样例输入
4 2
样例输出
7
数据规模与约定

对于30%的数据,KL <= 106

对于50%的数据,K <= 16, L <= 10;

对于100%的数据,1 <= K,L <= 100。

解答:

理解动态规划一个网址就够了:http://www.sohu.com/a/153858619_466939

练习:http://blog.csdn.net/qq_32400847/article/details/51148917

参考了这位博主的文章终于想通这道题可以怎么做。

对于动态规划的题目最重要的是会写状态转移方程,而写状态转移方程的关键就在于把问题分割成子问题。

假设已知l位k进制的k好数的末位为i个数为n(l,i),l位k进制的k好数的个数为nl,则当加一位数字在末位的l+1位k进制的k好数应该怎么算?

n(l+1,0)=nl-n(l,1)

n(l+1,i)=nl-n(l,i-1)-n(l,i+1)  其中1

n(l+1,k-1)=nl-n(l,k-2)

而问题的边界就是当l=1时。

有了状态转移方程和问题边界之后就可以开始编程了。显然需要一个长度为k的数组来保存l-1位的情况。而既要计算各种数的情况,又要计算不同位数的情况,最后还要求和,所以时间复杂度为o(n*n+n),即o(n*n)。

最终结果:

#include
using namespace std;
const long long int M=1000000007;

int main() {
	int k,l;
	cin>>k>>l;
	long long int a[100]={};
	long long int b[100]={};
	long long int sum=0;
	for(int i=1;i<=l;i++){
		if(i==1){
			//问题边界直接求 
			a[0]=0;
			for(int j=1;j<=k-1;j++){
				a[j]=1;
			}
			sum=(k-1)%M;
		}else{
			//为了节省存储空间,我只用了两个数组换着保存值。 
			if(i%2==0){
				//防止在相减的时候出现负值 
				long long int lastsum=sum+2*M;
				sum=0;
				b[0]=(lastsum-a[1])%M;
				sum+=b[0];
				for(int j=1;j<=k-2;j++){
					b[j]=(lastsum-a[j-1]-a[j+1])%M;
					sum+=b[j];
				}
				b[k-1]=(lastsum-a[k-2])%M;
				sum+=b[k-1];
			}else{
				long long int lastsum=sum+2*M;
				sum=0;
				a[0]=(lastsum-b[1])%M;
				sum+=a[0];
				for(int j=1;j<=k-2;j++){
					a[j]=(lastsum-b[j-1]-b[j+1])%M;
					sum+=a[j];
				}
				a[k-1]=(lastsum-b[k-2])%M;
				sum+=a[k-1];
			}
			sum=sum%M;
		}
	}
	cout<

写的不好轻点喷。。。


问题描述

有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了,那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?

输入格式

第一行包含一个整数 n 。

接下来的一行包含 n 个正整数,第 i 个正整数代表点 i 的权值。

接下来一共 n-1 行,每行描述树上的一条边。

输出格式
输出一个整数,代表选出的点的权值和的最大值。
样例输入
5
1 2 3 4 5
1 2
1 3
2 4
2 5
样例输出
12
样例说明
选择3、4、5号点,权值和为 3+4+5 = 12 。
数据规模与约定

对于20%的数据, n <= 20。

对于50%的数据, n <= 1000。

对于100%的数据, n <= 100000。

权值均为不超过1000的正整数。

解答:

所有的节点都有选择与不选择这两种状态,如果选择从下往上当作状态的方向的话,那么,下一层是否选择会影响上一层的选择情况,用dp[i][0]表示节点不选择,用dp[i][1]表示选择时该节点与其所有的后代的权值和,则

dp[i][0]=max(dp[j][0],dp[j][1])+...+max(dp[k][0],dp[k][1]);                   // j-k为i的孩子

dp[i][1]=i点权值+dp[j][0]+...+dp[k][0];    // j-k为i的孩子

问题的边界则为叶子节点。如果想要从下往上的话,我们就得尽快找到叶子节点,还得很快的根据此节点找到其兄弟和父节点。如果从上往下递归的话,可以使用一个二维数组保存所有节点的孩子,遍历孩子就行。

一直都忽略了树的方向性,其实找方向很简单,虽然这不是二叉树。只需要把任意一个叶子节点当作根节点就可以了,因为我保存的是边,没有明确哪个是父节点,所以我在递归的时候传入了一个last变量用来保存是哪个节点引发的递归,这样就可以知道谁是父节点了。

#include
#include
using namespace std;
int values[100001];
vector > edges;
int dp[100001][2];
int DP(int n,int select,int last=-1){
	
	if(dp[n][select]!=0){
		return dp[n][select];
	}
	if(select==0){
		for(int i=0;i>n;
	edges.resize(n+1);
	for(int i=1;i<=n;i++){
		cin>>values[i];
	}
	for(int i=0;i>j>>k;
		edges[j].push_back(k);
		edges[k].push_back(j);
	}
	
	cout<

最后用时竟然达到了500ms,写得不好轻点喷。。。

你可能感兴趣的:(蓝桥杯试题-算法篇1)