2014年蓝桥杯C++B组题解

一. 啤酒和饮料

在这里插入图片描述
暴力枚举

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
    
    
	int a, b;
	for(a = 1; a <= 43;a++)
	{
    
    
		for(b =1; b <= 35; b++)
		{
    
    
			if(a * 1.9 + b * 2.3 == 82.3 && b < a) cout <<a << " "<< b<< endl;
		}	
	}
	return 0;
} 

二.切面条

在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
#include<math.h>
int main()
{
    
    
    int n;
    double answer;
    scanf("%d",&n);
    answer=pow(2,n)+1;//调用数学库函数<math.h>里的平方根函数pow()
    printf("%d",(int)answer);//强制类型转换成整形
    return 0;
}

递归:

#include <iostream>
using namespace std;
int f(int n) {
    
    
    //基本递归 
    if(n == 0) {
    
    
        return 2;
    } else {
    
    
        return 2 * f(n - 1) - 1;
    }
}
int main(void) {
    
    
    cout << f(10) << endl;
    return 0;
}

三.李白打酒

话说大诗人李白,一生好饮。幸好他从不开车。

一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:

无事街上走,提壶去打酒。
逢店加一倍,遇花喝一斗。

这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。 

请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则:babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?请你计算出所有可能方案的个数(包含题目给出的)。
#include<iostream>
#include<algorithm>
using namespace std;
int ans;
void f(int d, int h, int j)//店,花,酒 
{
    
    
	if(d == 0 && h == 0 && j == 1)//最后一次遇到花,所以酒还剩1 
	ans++;
	
	if(d > 0) f(d-1, h, j*2);//逢店加一倍 
    if(h > 0) f(d, h-1,j-1);//逢花-1 
}
int main()
{
    
    
  	f(5,9,2);//已规定最后一次遇到的是花,所以只需搜索9次花 
  	cout << ans << endl;
  	return 0;
} 

四.史丰收速算

史丰收速算法的革命性贡献是:从高位算起,预测进位。不需要九九表,彻底颠覆了传统手算!

速算的核心基础是:1位数乘以多位数的乘法。

其中,乘以7是最复杂的,就以它为例。

因为,1/7 是个循环小数:0.142857...,如果多位数超过 142857...,就要进1

同理,2/7, 3/7, ... 6/7 也都是类似的循环小数,多位数超过 n/7,就要进n

下面的程序模拟了史丰收速算法中乘以7的运算过程。

乘以 7 的个位规律是:偶数乘以2,奇数乘以2再加5,都只取个位。

乘以 7 的进位规律是:
满 142857... 进1,
满 285714... 进2,
满 428571... 进3,
满 571428... 进4,
满 714285... 进5,
满 857142... 进6

请分析程序流程,填写划线部分缺少的代码。
#include<stdio.h>
#include<string.h>
//计算个位 
int ge_wei(int a)
{
    
    
	if(a % 2 == 0)
		return (a * 2) % 10;//偶数乘以2,保留个位 
	else
		return (a * 2 + 5) % 10;//奇数	乘以2加上5 保留个位 
}

//计算进位 
int jin_wei(char* p)
{
    
    
	char* level[] = {
    
    
		"142857",
		"285714",
		"428571",
		"571428",
		"714285",
		"857142"
	};// 多位数超过n/7,就要进n 
	
	char buf[7];
	buf[6] = '\0';
	strncpy(buf,p,6);//将p这个字符串前六个字符,拷贝到buf 
	
	int i;
	for(i=5; i>=0; i--){
    
    
		int r = strcmp(level[i], buf);//从后往前依次取level中的串和buf比较 
		if(r<0) return i+1;//buf更大 , 得出进位数位i+1 
		while(r==0){
    
      // level和buf相同 
			p += 6;   //往后偏移六位 
			strncpy(buf,p,6);//再拷贝6个字符到buf中 
			r = strcmp(level[i], buf);//再比较 
			if(r<0) return i+1;//buf更大 
			
			//填空
			if(r > 0) return i;//如果buf更小 
		}
	}
	
	return 0;
}

//多位数乘以7
void f(char* s) //s是多位数 
{
    
    
	int head = jin_wei(s);// head是s的进位 
	if(head > 0) printf("%d", head);//输出进位 
	
	char* p = s;//拷贝字符串指针 
	while(*p){
    
    //指针没有到末尾 
		int a = (*p-'0');//一次取字符,将字符转数字 
		int x = (ge_wei(a) + jin_wei(p+1)) % 10;//个位加上后续字符串进位 取个位 
		printf("%d",x);
		p++;//指针后移 
	}
	
	printf("\n");
}

int main()
{
    
    
	f("428571428571");
	f("34553834937543");		
	return 0;
}

五.打印图形

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

#include<stdio.h>
#include<string.h>
#define N 70

void f(char a[][N], int rank, int row, int col)
{
    
    
	if(rank==1){
    
    //
		a[row][col] = '*';
		return;
	}
	
	int w = 1;
	int i;
	for(i=0; i<rank-1; i++) w *= 2;//w = 32
	
	//把三角形分三个区域,找三个顶点
	f(a, rank-1, row, col+w/2);//填空 a,5,0,16,处理顶上的三角形
	f(a, rank-1, row+w/2, col);//a,5,16,0处理左下角的三角形
	f(a, rank-1, row+w/2, col+w);//a,5,16,16处理右下角的三角形
}

int main()
{
    
    
	char a[N][N];
	int i,j;
	for(i=0;i<N;i++)
	for(j=0;j<N;j++) a[i][j] = ' ';
	
	f(a,6,0,0);
	
	for(i=0; i<N; i++){
    
    
		for(j=0; j<N; j++) printf("%c",a[i][j]);
		printf("\n");
	}
	
	return 0;
}

六.奇怪的分式

上小学的时候,小明经常自己发明新算法。一次,老师出的题目是:

1/4 乘以 8/5 

小明居然把分子拼接在一起,分母拼接在一起,答案是:18/45 (参见图1.png)

老师刚想批评他,转念一想,这个答案凑巧也对啊,真是见鬼!

对于分子、分母都是 1~9 中的一位数的情况,还有哪些算式可以这样计算呢?

请写出所有不同算式的个数(包括题中举例的)。

显然,交换分子分母后,例如:4/1 乘以 5/8 是满足要求的,这算做不同的算式。

但对于分子分母相同的情况,2/2 乘以 3/3 这样的类型太多了,不在计数之列!

在这里插入图片描述
暴力枚举
注意:将除法转化为乘法

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
    
    
	int cnt = 0;
	for(int i = 1; i <= 9; i++)
      for(int j = 1;j <= 9; j++)
       for(int a = 1;a <= 9; a++)
         for(int b = 1; b <= 9; b++)
         {
    
    
         	if(i!= j && a != b &&(j*b)*(i*10 + a) == (i*a)*(j*10+ b))
         	{
    
    
			 cnt++;
         	 cout << i <<"/"<<j <<" " << a <<"/" << b<<endl;
            }
		 }
		 cout << cnt;
		 return 0;
}

七.六角填数

在这里插入图片描述

如图【1.png】所示六角形中,填入1~12的数字。

使得每条直线上的数字之和都相同。

图中,已经替你填好了3个数字,请你计算星号位置所代表的数字是多少?

全排列:
在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
void check(vector<int> A)
{
    
    
	int r1 = 1 + A[0] + A[3] +A[5];
	int r2 = 1 + A[1] + A[4] +A[8];
	int r3 = 8 + A[0] + A[1] +A[2];
	int r4 = 11 + A[3] + A[6];
	int r5 = 3 + A[2] + A[4] +A[7];
	int r6 = A[5] + A[6] + A[7] +A[8];
	if(r1 == r2 && r2 == r3 && r3 == r4 && r4 == r5 && r5 == r6)
	{
    
    
		for(int i =0 ;i < 9; ++i)
		{
    
    
			cout << A[i] << " " ;
		}
		cout << endl;
		cout << A[3];
	}
}
int main()
{
    
    
	vector<int> A;
	A.push_back(2);
	for(int i = 4;i <= 7; ++i)
	{
    
    
		A.push_back(i);
	}
	for(int i = 9; i <= 12; ++i)
	{
    
    
		A.push_back(i);
	}
	do
	{
    
    
		check(A);
	}while(next_permutation(A.begin(), A.end()));
	return 0;
}

八.蚂蚁感冒

长100厘米的细长直杆子上有n只蚂蚁。它们的头有的朝左,有的朝右。 

每只蚂蚁都只能沿着杆子向前爬,速度是1厘米/秒。

当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。

这些蚂蚁中,有1只蚂蚁感冒了。并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。

请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。

【数据格式】

第一行输入一个整数n (1 < n < 50), 表示蚂蚁的总数。

接着的一行是n个用空格分开的整数 Xi (-100 < Xi < 100), Xi的绝对值,表示蚂蚁离开杆子左边端点的距离。正值表示头朝右,负值表示头朝左,数据中不会出现0值,也不会出现两只蚂蚁占用同一位置。其中,第一个数据代表的蚂蚁感冒了。

要求输出1个整数,表示最后感冒蚂蚁的数目。

例如,输入:

3
5 -2 8

程序应输出:

1

再例如,输入:

5
-10 8 -20 12 25

程序应输出:

3
这个题乍一看脑袋里好像很乱,其实理清头绪后再敲出来就很容易了。首先一定要记住蚂蚁都具有相同的速度,所以他们的相对位置不会变!

  我们可以从第一只那只感冒的蚂蚁入手,实际上,如果第一只蚂蚁向右爬,则它出发的位置右边的蚂蚁中向左爬且离第一只蚂蚁最近的那只会被传染,然后双方会掉头爬行,接下来,第一只蚂蚁左边的蚂蚁中向右爬行且离第一只蚂蚁最近的那只会被传染,双方再掉头。而对于一开始第一只被传染的那只蚂蚁来说,它会传染给他右边向左爬且最近的蚂蚁……
  
  所以,将问题简化,实际上就是向右爬的第一只感冒蚂蚁会先传染它右边所有向左爬的蚂蚁,掉头向左爬时会传染给左边所有向右爬的蚂蚁。
  同理,若第一只蚂蚁开始时是向左爬,被传染的蚂蚁=左边所有向右爬的蚂蚁+右边所有向左爬的蚂蚁。
  
  注意这种思想需要有个第一只感冒的蚂蚁是否会掉头的判断,因为第一只蚂蚁向左(或右)爬的时候,若不会遇到一只迎面而来的蚂蚁使它掉头的话,则即使右边有向左爬的蚂蚁,也不会被其传染。

在这里插入图片描述

向右:同向的情况下,右边的追不上,左边的不一定(取决于前面有没有蚂蚁碰面)
反向,右边的一定会感冒,左边的一定不感冒(早就出去了)

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

int main()
{
    
    
	int n;
	cin >> n;
	int a[n];
	for(int i = 0; i < n; i++) cin >> a[i];
	int x = a[0];//以头号元素为基准 

	if(x > 0)//向右走
	{
    
    
		int ans = 1; //第一个是感冒的 
		for(int i = 0; i < n; i++) //扫描所有蚂蚁
		{
    
    
			if(a[i] <0 && -a[i] > x)//反向,右边的一定会感冒 
			ans++; 
		} 
		
		//判断x的左边 同向的情况下 ,是否有反向的 
		if(ans != 1)//有对头的 
		 for(int i = 0;i < n; i++)
		 {
    
    
		 	if(a[i] > 0 && a[i] < x) //与x同向的 在x左边的 
		  ans++; 
		 }
		 cout << ans << endl;
	 } 
	 if(x <0)//向左 
	 {
    
    
	 	
		 int ans = 1;
		 for(int i = 0; i < n; ++i)
		 {
    
    
		 	if(a[i] > 0 && a[i] < -x)//左侧 从左到右的
		 	 ans++;
		 } 
		 
		 if(ans != 1)//有对头的
		   for(int i = 0; i <n; ++i)
		   {
    
    
		   	if(a[i] < 0 && -a[i] > -x)
		   	ans++;
			} 
			cout << ans << endl;
	 } 
	return 0;
}

九.地宫取宝

X 国王有一个地宫宝库。是 n x m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。

地宫的入口在左上角,出口在右下角。

小明被带到地宫的入口,国王要求他只能向右或向下行走。

走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。

当小明走到出口时,如果他手中的宝贝恰好是k件,则这些宝贝就可以送给小明。

请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这k件宝贝。

【数据格式】

输入一行3个整数,用空格分开:n m k (1<=n,m<=50, 1<=k<=12)

接下来有 n 行数据,每行有 m 个整数 Ci (0<=Ci<=12)代表这个格子上的宝物的价值

要求输出一个整数,表示正好取k个宝贝的行动方案数。该数字可能很大,输出它对 1000000007 取模的结果。

例如,输入:

2 2 2
1 2
2 1

程序应该输出:

2

再例如,输入:

2 3 2
1 2 3
2 1 5

程序应该输出:

14

深搜

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int MOD = 1000000007;
int n,m, k;
int d[50][50]; 
long long ans;
void dfs(int x, int y, int max, int cnt)
{
    
    
	
	if(x == n || y== m || cnt > k) return;
	int cur = d[x][y];
	if(x == n - 1 && y == m-1)//已经面临最后一个格子
	{
    
    
		if(cnt == k) 
		{
    
    
			ans++;
			if(ans > MOD) ans %= MOD;
		}
		if(cnt == k - 1 &&cur > max) {
    
    
			ans++;
			if(ans > MOD) ans %= MOD;
		}
	} 
	
	if(cur > max)//可以取这个物品 
	{
    
    
		dfs(x, y+1, cur, cnt + 1);//向 右走 
		dfs(x+1, y, cur, cnt + 1);//向下走 
	}
	//对于价值较小,或大不取物品
	 dfs(x, y+1, max, cnt);//向 右走 
	 dfs(x+1, y, max, cnt);
}
int main()
{
    
    
    cin >> n >> m >> k;
    for(int i = 0; i < n ; i++)
     for(int j = 0;j < m; j++)
      {
    
    
      	cin >> d[i][j];
	  }
	  dfs(0, 0, -1, 0);//第一个点的价值可能是0 
	  cout << ans << endl;
	  return 0;
}

记忆性递归

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int MOD = 1000000007;
int n,m, k;
int d[50][50]; 
long long ans;
long long cache[50][50][14][13];

long long dfs2(int x, int y, int max, int cnt)//记忆性递归 
{
    
    
	if(cache[x][y][max + 1][cnt] != -1) return cache[x][y][max + 1][cnt];
	long long ans = 0;
	
	if(x == n || y== m || cnt > k) return 0;
	int cur = d[x][y];
	if(x == n - 1 && y == m-1)//已经面临最后一个格子
	{
    
    
		if(cnt == k) 
		{
    
    
			ans++;
			if(ans > MOD) ans %= MOD;
		}
		if(cnt == k - 1 &&cur > max) {
    
    
			ans++;
			if(ans > MOD) ans %= MOD;
		}
		return ans;
	} 
	
	if(cur > max)//可以取这个物品 
	{
    
    
		ans += dfs2(x, y+1, cur, cnt + 1);//向 右走 
		ans += dfs2(x+1, y, cur, cnt + 1);//向下走 
	}
	//对于价值较小,或大不取物品
	 ans += dfs2(x, y+1, max, cnt);//向 右走 
	 ans += dfs2(x+1, y, max, cnt);
	 
	 cache[x][y][max+1][cnt] = ans%MOD;
	 return ans % MOD; 
}
int main()
{
    
    
    cin >> n >> m >> k;
    for(int i = 0; i < n ; i++)
     for(int j = 0;j < m; j++)
      {
    
    
      	cin >> d[i][j];
	  }
	  memset(cache,-1,sizeof cache);//初始化为-1 
	  cout << dfs2(0, 0, -1, 0) << endl;
	  return 0;
}

十.小朋友排队

n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。

每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。

如果某个小朋友第一次被要求交换,则他的不高兴程度增加1,如果第二次要求他交换,则他的不高兴程度增加2(即不高兴程度为3),依次类推。当要求某个小朋友第k次交换时,他的不高兴程度增加k。

请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。

如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。

【数据格式】

输入的第一行包含一个整数n,表示小朋友的个数。
第二行包含 n 个整数 H1 H2 … Hn,分别表示每个小朋友的身高。
输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。

例如,输入:

3
3 2 1

程序应该输出:

9

【样例说明】
首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。

【数据规模与约定】
对于10%的数据, 1<=n<=10;
对于30%的数据, 1<=n<=1000;
对于50%的数据, 1<=n<=10000;
对于100%的数据,1<=n<=100000,0<=Hi<=1000000。
树状数组(参考 来自xxx0624)

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 100010;
int h[N],c[N];
long long cnt[N];//cnt记录每个孩子被交换的次数
int lowbit(int n)
{
    
    
	return n-(n&(n-1));
}
//原始数组的i位置增加v后,更新c数组 
void updata(int n, int i,int v, int c[])
{
    
    
	for(int k = i; k <= n; k+= lowbit(k))
	{
    
    
		c[k] += v;
	}
 } 
int getSum(int c[], int i)
{
    
    
	int sum = 0;
	for(int k = i;k >= 1; k -= lowbit(k))
	{
    
    
		sum += c[k];
	}
	return sum;
 } 
 
int main()
{
    
    
   int n;
   cin >> n;
   
   memset(cnt, 0, sizeof(cnt)) ;
   
    int maxH = -1;
   for(int i = 0; i < n; ++i)
   {
    
    
   	cin >> h[i];
   	if(h[i] > maxH) maxH = h[i];
   }
    
    memset(c, 0, sizeof(c)) ;
    
   //从左侧开始找 
   for(int i = 0; i <n; ++i)
   {
    
    
   	updata(maxH + 1, h[i] + 1, 1,c);//在相应位置计数变为1, 用树状数组维护数据出现的个数
	
	int sum = getSum(c, h[i] + 1);//小于等于h[i]+1大数据的个数 
   	cnt[i] += (i+1) - sum;//得到的就是当前数据左侧比数据大的个数 
   }
   
    memset(c, 0, sizeof(c)) ;
   //从右侧开始 
   for(int i = n-1; i >= 0; --i)
   {
    
    
   	updata(maxH + 1, h[i] + 1, 1,c);//在相应位置计数变为1, 用树状数组维护数据出现的个数
	
//	int sum = getSum(c, h[i] + 1);//小于等于h[i]+1大数据的个数 
// 	int self = getSum(c, h[i] + 1) - getSum(c, h[i]);
//	cnt[i] += sum - self;//上一步求出小于等于h的个数,扣掉自己的个数,得到的就是当前数据右侧比数据大的个数 
    cnt[i] += getSum(c,h[i]);
   }
   long long ans = 0;
   for(int i = 0; i < n; ++i)
   {
    
    
   	ans +=(cnt[i]*(cnt[i] + 1)/2);
   }
   cout << ans;
   return 0;
}

猜你喜欢

转载自blog.csdn.net/Annabel_CM/article/details/112762328