2019年蓝桥杯B组C/C++ 省赛

å¨è¿éæå¥å¾çæè¿°

å¨è¿éæå¥å¾çæè¿°

使用的数据如下:

97 90 0   0   0 
92 85 96 0   0 
0   0   0   0   93 
0   0   0   80 86 
89 83 97 0   0 
82 86 0   0   0 
0   0   0   87 90 
0   97 96 0   0 
0   0   89 0   0 
95 99 0   0   0 
0   0   96 97 0 
0   0   0   93 98 
94 91 0   0   0 
0   83 87 0   0 
0   0   98 97 98 
0   0   0   93 86 
98 83 99 98 81 
93 87 92 96 98 
0   0   0   89 92 
0   99 96 95 81 

思路:使用DFS搜索+回溯的方法解题。注意 每个队员只能使用一次  因此不能直接找1 2 3 4 5位置上最大能力后相加,因为可能一个人在多个位置上都是第一。这样会重复使用一个人(怪怪的)。

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

bool vis[25];
int MaxSum,Ability[25][10]; 

void DFS(int locate,int sum){ // 当前为locate位置寻找人选 
	if( locate == 6 ){
		MaxSum = max(MaxSum, sum);
		return;
	}
	for(int i = 1 ; i <= 20 ; ++ i){
		if( !vis[i] ){
			vis[i] = true;
			DFS(locate+1, sum + Ability[i][locate]);
			vis[i] = false; 
		}
	}
} 

int main(){
	for( int  i = 1; i <= 20; ++ i){
		cin >> Ability[i][1] >> Ability[i][2] >> Ability[i][3] >> Ability[i][4] >> Ability[i][5] ;
	}
	DFS(1,0);
	cout << MaxSum;
	return 0;
}

å¨è¿éæå¥å¾çæè¿°


思路:类似26进制的一道题,但是不完全一样,本题中的规则不允许0的存在,其实就是要求我们给出一种26幂次的分解方案,使得对于数num可以表示为:

                             num=p_0*(26)^0+p_1*(26)^1+...+p_k*(26)^k,1<=p_i<=26

#include<bits/stdc++.h>
using namespace std;
int main(){
	int k = 1,num = 2019;
	while( num > k ){
		k *= 26;
	}
	k /= 26;
	while( num ){
		cout << (char)('A' + num/k - 1);
		num %= k;
		k /= 26;
	}
	return 0;
}

å¨è¿éæå¥å¾çæè¿°

思路:斐波那契数列的变种题,项太多,如果硬要存储需要用到高精度,但是值得注意的是,本题只要最后的四位数字,实际上,在加法过程中,在第五位及以上的高位的运算,不管怎么样都不会影响到低四位的,因此我们只需要保留每个结果的低四位即可。

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

const int MaxN = 20190400;

int dp[MaxN];

int main(){
	dp[1] = dp[2] = dp[3] = 1;
	for(int i = 4; i <= 20190324; ++ i){
		dp[i] = (dp[i-1] + dp[i-2] + dp[i-3]) % 10000;
	}
	cout << dp[20190324] % 10000;
	return 0;
} 

å¨è¿éæå¥å¾çæè¿°

思路:题意即要求一组x,y,z使得x+y+z=2019,并且题中明说,交换3的整数的顺序视为一种方法,因此我们可以规定x<y<z,此时必定可以不重不漏。

技巧:实际上根据等式x+y+z=2019,我们只需要知道x和y,就可以确定z,因此不需要三重循环。

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

bool isLegal(int num){
	while( num ){
		if( num % 10 == 2 || num % 10 == 4)return false;
		num /= 10;
	}
	return true;
}

int main(){
	int ans = 0, x, y, z;
	for( x = 1; x <= 2019; ++ x){
		if( !isLegal(x) )continue;
		for( y = x + 1; 2019 - x - y > y &&  y <= 2019; ++ y){
			if( !isLegal(y) || !isLegal(2019 - x - y) )continue;
			++ ans;
		}
	}
	cout << ans; 
	return 0;
}

å¨è¿éæå¥å¾çæè¿°

å¨è¿éæå¥å¾çæè¿°

å¨è¿éæå¥å¾çæè¿°

思路:一道带有条件约束的最短路问题,要求最小字典序,我们只需要在一次做BFS四个方向扩展的时候,按照字典序的大小顺序做先后扩展即可,即先左(L)再右(R)再下(D)再上(U)。

/*
测试数据
01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000
*/
#include<bits/stdc++.h>
using namespace std;

struct Node{
	int x,y;
	string status;	
};
char Map[100][100];
bool vis[100][100];
queue<Node> que;

string BFS(int sx,int sy,int n,int m){
	Node node;
	string state;
	vis[sx][sy] = true;
	que.push(Node{sx,sy,""});
	while(!que.empty()){
		node = que.front();
		que.pop();
		sx = node.x, sy = node.y,state = node.status;
		
		if( sx == n && sy == m ){
			return state;
		}
		
		if( sx  < n && Map[sx + 1][sy] == '0' && !vis[sx + 1][sy] ){ //向下走  
			vis[sx + 1][sy] = true;
			que.push(Node{sx + 1,sy,state + "D"});
		}
		
		if( sy  > 1 && Map[sx][sy - 1] == '0' && !vis[sx][sy - 1] ){ //向左走  
			vis[sx][sy - 1] = true;
			que.push(Node{sx,sy - 1,state + "L"});
		}
		
		if( sx  < m && Map[sx][sy + 1] == '0' && !vis[sx][sy + 1] ){ //向右走  
			vis[sx][sy + 1] = true;
			que.push(Node{sx,sy + 1,state + "R"});
		}
		
		if( sx  > 1 && Map[sx - 1][sy] == '0' && !vis[sx - 1][sy] ){ //向上走  
			vis[sx - 1][sy] = true;
			que.push(Node{sx - 1,sy,state + "U"});
		}
		
	}
}

int main(){
	int n = 30,m = 50,i,j;
	for( i = 1; i <= n; ++ i){
		cin >> Map[i] + 1;
	}
	cout << BFS(1,1,30,50);
	return 0;
}

å¨è¿éæå¥å¾çæè¿°

å¨è¿éæå¥å¾çæè¿°

思路:本题的数据量很小,可以使用直接枚举的方法。还有一种类似的问题,比如统计

直接枚举法:

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

bool isInteresting(int num){
	while( num ){
		if( num % 10 == 2 || num % 10 == 0 || num % 10 == 1 || num % 10 == 9)return true;
		num /= 10;
	}
	return false;
}

int main(){
	int n, ans=0;
	cin >> n;
	for( int i = 1; i <= 40; ++ i){
		if(isInteresting(i)) ans += i;
	} 
	cout << ans;
	return 0;
}

å¨è¿éæå¥å¾çæè¿°

å¨è¿éæå¥å¾çæè¿°

思路:由于完全二叉树的性质,要么它是一棵完全二叉树,要么只有它的最后一层不满,且叶子一定是从左到右的。于是我们可以直接按照这个规则进行统计即可。

#include<bits/stdc++.h>
using namespace std;
const int MaxN = 100010;
int Arr[MaxN];

int main(){
	int n,i = 1,j,k,MinFloor = 1,floor = 1,len = 1,MaxSum = 0,sum = 0;
	cin >> n;
	for( i = 1; i <= n ; ++ i ){
		cin >> Arr[i];
	}
	i = 1;
	while( i <= n ){
		sum = 0;
		for( k = 1 ; i <= n && k <= len; ++ k, ++ i ){
			sum += Arr[i];
		}
		if( sum > MaxSum ){
			MaxSum = sum;
			MinFloor = floor;
		}
		len <<= 1;
		++ floor;
	}
	cout << MinFloor ;
	return 0;
}

å¨è¿éæå¥å¾çæè¿°

思路:由于这是一个等差数列中取出的一部分,因此当给他们排序后(升降序无所谓的,虽然会将公差变为负数,但是题目问的是有多少项,这取决于最小值与最大值的差值与公比绝对值的比值。设公比的最大值为d,则排序之后,相邻数之间的差值为i*d,我们要找d,其实就是给这些差值找最大公因数,即为最大公比。

证明:如果通过最大公因数找出的答案不是最大公比,而是m*d,则与原设的最大公比为d矛盾。

这里特别强调一下最大公比,因为1 3 5 9可能是公比为2的数列中的一部分,也可能为公比为1的数列中的一部分,题目要求的是最短序列,自然就要求最大的公比。

#include<bits/stdc++.h>
using namespace std;
const int MaxN = 1e5+10;
int Arr[MaxN];

int gcd(int A,int B){
	while(B^=A^=B^=A%=B);
	return A;
}

int main(){
	int n,i,d;
	cin >> n;
	for( i = 1; i <= n; ++ i )cin >> Arr[i];
	sort(Arr + 1,Arr + 1 + n);
	d = Arr[2] - Arr[1];
	if(d == 0){
		cout << n ;
		return 0;
	}
	for( i = 3; i <= n; ++ i){
		d = gcd(d, Arr[i] - Arr[i - 1]);
	}
	cout << (Arr[n] - Arr[1]) / d + 1;
	return 0;
}

注意:需考虑原数列为常数数列也就是d=0的情况,此时我们直接输出n即可。

å¨è¿éæå¥å¾çæè¿°

思路:题意看着吓人但是仔细想想,数的位置、加减符号的位置都可以自行安排,那么实际上直接贪心就好,由于我们有N个加号,因此我们可以将较大的N+1个数累加起来减去剩余的较小的M个数,即为答案。

#include<bits/stdc++.h>
using namespace std;
const int MaxN = 2e5+10;
int Arr[MaxN];

int main(){
	int N,M,i,ans = 0;
	cin >> N >> M;
	for( i = 1; i <= N + M + 1; ++ i){
		cin >> Arr[i];
	}
	sort(Arr + 1,Arr + N + M + 2);
	for( i = N + M + 1; i >= 1; -- i, -- N){
		if( N >= 0){
			ans += Arr[i]; 
		}else{
			ans -= Arr[i]; 
		}
	}
	cout << ans; 
	return 0;
}

呵呵呵呵呵,这么想就完全上了他们的大逼当了!!!!想当年这道题我也是一眼看上去多简单啊!!!!然后返程的时候学长说,后缀表达式里已经考虑了运算符优先级,不能一股脑把加法和减法当作是左结合运算符来看。什么意思呢?

我们看这样一个中缀算式:3-(1-2),如果我们用后缀表达式表达它,那么就会变成3\ 1\ 2\ -\ -,括号没有了!!!!也就是说一个后缀表达式的式子,在它是中缀表达式的时候,是有可能有括号的!!!!

由于有隐含括号的存在,因此我们知道,每有两个负号,我们就可以选择一个数加上、选择一个数减去,因此,真正可以类加上的数的个数为N+1+[M/2],而需要减去的数的个数为M-[M/2]。

#include<bits/stdc++.h>
using namespace std;
const int MaxN = 2e5+10;
int Arr[MaxN];

int main(){
	int N,M,i,ans = 0;
	cin >> N >> M;
	for( i = 1; i <= N + M + 1; ++ i){
		cin >> Arr[i];
	}
	sort(Arr + 1,Arr + N + M + 2);
	i = N + M + 1;
	N += (M >> 1);
	for( ; i >= 1; -- i, -- N){
		if( N >= 0){
			ans += Arr[i]; 
		}else{
			ans -= Arr[i]; 
		}
	}
	cout << ans; 
	return 0;
}

å¨è¿éæå¥å¾çæè¿°

å¨è¿éæå¥å¾çæè¿°

样例2输出:

5

7

4

思路:偷的闫老的想法2333   视频链接:https://www.bilibili.com/video/av47356111

首先来理解一下题意,其实无论a_i>0还是a_i<0,在i处进行灵能传输的后果就是原序列a_{i-1},a_{i},a_{i+1}变为新序列a_{i-1}+a_i,-a_i,a_{i+1}+a_i

我们看看在这个过程中的不变量,很明显,这三者的和不变,另外,由于其他数没有动过,易推知整个序列总合不变。此时将前缀和数组引入 (非常难以想到,但是是关键一步)。

                                                                               S_i=\sum_{j=1}^{i}a_j

可以推知,在传输前,前缀和序列为S_1,S_2,...,S_{i-1},S_i,S_{i+1},...,S_n。当i处发生了灵能传输后,前缀和序列变为:

                                                               S_1,S_2,...,S_{i},S_{i-1},S_{i+1},...,S_n

于是,当我们引入前缀和数组后,灵能传输的步骤就被简化为交换S_{i-1}S_i

根据前缀和数组的构成方式,我们知道a_i=S_i-S_{i-1},于是题目变为交换前缀和数组中的相邻元素,使得

                                          max(|S_1|,|S_2-S_1|,...,|S_i-S_{i-1}|,|S_{i+1}-S_i|,...,|S_n-S_{n-1}|)

能够达到最小值。[注]此处所写的S_i的i表示是经过交换后达到最终状态的i。

为了统一问题的结构,我们引入S_0=0,于是题目变成交换i与i-1( 2<=i<=n-1)[另一种理解:实际上也就是索引处在1~n-1内的数可以任意交换位置],使得:

                                     max(|S_1-S_0|,|S_2-S_1|,...,|S_i-S_{i-1}|,|S_{i+1}-S_i|,...,|S_n-S_{n-1}|)

拥有最小值。

讨论如下情况:预设\dpi{100} S_0<S_n,大于的情况无非就是序列反过来,不影响

1. S_0为最小值,S_n为最大值,此时若整个序列单调递增,则S_i-S_{i-1}有最小值

证明:在整个序列单调的情况下,现在我们交换其中的i与i-1,在交换前,这部分序列为S_{i-1},S_i,S_{i+1},交换后变为S_{i},S_{i-1},S_{i+1},于是交换前的绝对值差值为|S_{i}-S_{i-1}|,|S_{i+1}-S_{i}|,交换后为|S_{i-1}-S_{i}|,|S_{i+1}-S_{i-1}|,由于原序列为单调递增的,因此

                             |S_{i+1}-S_{i-1}|=|S_{i+1}-S_i|+|S_{i}-S_{i-1}|>=|S_{i+1}-S_i|,(|S_i-S_{i-1}|>=0)

等于号仅在S_i==S_{i-1}时成立。因此对于单调的序列,交换后不会使得答案变小,而很有可能使得答案变大,故原序列单调递增为最优解,该贪心方法正确。

2. 考虑一般的情况,S_0不为最小值,S_n不为最大值

那么根据上面的经验,我们也可以推知,整个序列可以被划分为三段(不能直接对整体进行排序是因为,我们必须要从S_0出发,结束于S_n,因为这两个位置是无法变动的)。于是很显然,在这种情况下,S_i的行走路径为:

这个图,显然没办法处理啊。。。因为我们完全没办法通过这个图知道如何从S_0到达Min,也无从知道如何从MaxS_n

但是我们可以知道,我们将S_1~S_{n-1}直接排序后,让S_0直接走到Min,让Max直接走到S_n是不太对的。

如:0 -1 -2 -3 1,如果我们将中间部分排序,则会得到0 -3 -2 -1 1,此时差值的最大绝对值为3,但是如果我们安排为0 -2 -3 -1 1则差值的最大绝对值仅有2。也就是说:

这样的走法往往是不正确的,而跳跃着走可以得到更小的答案,但是要跳多少呢。

我们可以将所有的数据压到Y轴上,这样我们就得到了一个递增序列。

现在我们的思路转换为,在这样一条数轴上,找到一种行走方式,使得每次跨点所行走的距离的最大值最小。

啥思路但是我们可以试一试!

首先每次走一步:

很明显,此种行走方式,在没有到达端点之前都不错,但一旦到了端点,下一步只能跨越一个非常大的距离!

每次间隔一个走,S0往左,S1往右,每次同时走:

可以看到,这种情况下,每次走的步伐最长为2个点间距,最短为1个点间距。

每次间隔两个个走,S0往左,S1往右,每次同时走:这边应该就不用画了吧,如果没有发生越界,就是三个点间距,如果发生了,可能是一个,可能是两个,因此,间隔一个走为正确答案。(说的云里雾里的,推荐大家看视频吧=w=)。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MaxN = 3e6+10;

LL S[MaxN],SS[MaxN];
bool used[MaxN];

int main() {
	int T,n,i,l,r;
	LL s0,sn,ans;
	cin >> T;
	while(T -- ){
		cin >> n;
		S[0] = 0;
		for( i = 1; i <= n; ++ i){
			cin >> S[i]; 
			S[i] += S[i-1];
		}
		s0 = 0, sn = S[n];
		if( s0 > sn ) swap(s0,sn);
		sort(S,S + n + 1);
		for( i = 0; i <= n ; ++ i){// 找到起点 
			if( s0 == S[i] ){
				s0 = i;
				break;
			}
		}
		for( i = 0; i <= n ; ++ i){//  找到终点 
			if( sn == S[i] ){
				sn = i;
				break;
			}
		}
		l = 0; r = n;
		memset(used,0,sizeof(used));
		for( i = s0; i >= 0; i -= 2 ){
			SS[l ++] = S[i];
			used[i] = true;
		}
		for( i = sn; i <=n ; i += 2){
			SS[r --] = S[i];
			used[i] = true;	
		}
		for( i = 0; i <=n ; ++ i ){
			if( !used[i] )SS[l ++] = S[i];
		}
		ans = S[1] - S[0];
		for(i = 2; i <= n; ++ i){
			ans = max(ans,abs(SS[i] - SS[i-1]));
		}
		cout << ans << endl;
	} 
	return 0;
}

总结:相当变态的数学题=m=(yue)

另外,这边使用bool数组排去了奇偶性判定的问题。我们让从起点开始往左的部分顺序存储在变换后的数组头部,让从终点往右的部分逆序存储在变换后的数组尾部,剩下的部分是从左边向右边的,直接用bool数组将还没有使用过的数,从左到右串起来即可。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值