蓝桥杯省赛训练2->2014年第五届蓝桥杯省赛B组

1.啤酒和饮料
啤酒每罐2.3元,饮料每罐1.9元。小明买了若干啤酒和饮料,一共花了82.3元。

我们还知道他买的啤酒比饮料的数量少,请你计算他买了几罐啤酒。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
using namespace std;
int main(){
	double price1=2.3,price2=1.9;
	for(int i=0;i<50;i++){
		for(int j=i+1;j<50;j++){
			if(fabs(price1*i+price2*j-82.3)<=1e-6){
				cout<<i<<' '<<j<<endl;
				return 0;
			}
		}
	}
}
//11
2.切面条
一根高筋拉面,中间切一刀,可以得到2根面条。
如果先对折1次,中间切一刀,可以得到3根面条。
如果连续对折2次,中间切一刀,可以得到5根面条。

那么,连续对折10次,中间切一刀,会得到多少面条呢?

//推一下可知 
//2,3,5,9......
//即:2^0+1,2^1+1,2^2+1,2^3+1......
//直接算出答案:1025
3.李白打酒
话说大诗人李白,一生好饮。幸好他从不开车。
一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:

无事街上走,提壶去打酒。

逢店加一倍,遇花喝一斗。

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

请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则:babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?请你计算出所有可能方案的个数(包含题目给出的)。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int count0,cnt;
void dfs(int step,int sum,int pre){//步数 酒量 前一步状态(1:店 0:花)
	if(step==16){
		if(count0==10&&sum==0&&pre==0){
			cnt++;
		}
		return;
	}
	count0++;
	dfs(step+1,sum-1,0);
	count0--;
	dfs(step+1,sum*2,1);
}
int main(){
	dfs(1,2,0);
	cout<<cnt<<endl;
}
//14
4.史丰收速算
史丰收速算法的革命性贡献是:从高位算起,预测进位。不需要九九表,彻底颠覆了传统手算!
速算的核心基础是: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
请分析程序流程,填写划线部分缺少的代码。
//计算个位 
int ge_wei(int a){
if(a % 2 == 0)
return (a * 2) % 10;
else
return (a * 2 + 5) % 10;
}
//计算进位 
int jin_wei(char* p){
char* level[] = {
"142857",
"285714",
"428571",
"571428",
"714285",
"857142"
};

char buf[7];
buf[6] = '\0';
strncpy(buf,p,6);

int i;
for(i=5; i>=0; i--){
int r = strcmp(level[i], buf);
if(r<0) return i+1;
while(r==0){
p += 6;
strncpy(buf,p,6);
r = strcmp(level[i], buf);
if(r<0) return i+1;
______________________________;  //填空
}
}

return 0;
}
//多位数乘以7
void f(char* s) {
int head = jin_wei(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;
}

答案:if(r>0) return i;

注意题目中的...指的是循环小数

5.打印图形



答案:f(a,rank-1,row,col+w/2)

考察递归,此时rank的图形由三个rank-1的图形拼凑而成

6.奇怪的分式
上小学的时候,小明经常自己发明新算法。一次,老师出的题目是:
1/4 乘以 8/5 
小明居然把分子拼接在一起,分母拼接在一起,答案是:18/45 (参见图1.png)
老师刚想批评他,转念一想,这个答案凑巧也对啊,真是见鬼!
对于分子、分母都是 1~9 中的一位数的情况,还有哪些算式可以这样计算呢?
 请写出所有不同算式的个数(包括题中举例的)。
显然,交换分子分母后,例如:4/1 乘以 5/8 是满足要求的,这算做不同的算式。
但对于分子分母相同的情况,2/2 乘以 3/3 这样的类型太多了,不在计数之列!


//注意:由此题答案可知,相加的两个分式交换位置看做不同的情况

#include <cstdio>
#include <iostream>
using namespace std;
int judge(int a[]){
	int n1,n2,n3,n4;
	if(a[0]==a[1]&&a[2]==a[3]){
		return 0;
	}
	n1=a[0]*a[2];
	n2=a[1]*a[3];
	n3=a[0]*10+a[2];
	n4=a[1]*10+a[3];
	if(n1*n4==n2*n3){
		return 1;
	}
	return 0;
}
int main(){
	int cnt=0,a[4];
	for(a[0]=1;a[0]<=9;a[0]++){
		for(a[1]=1;a[1]<=9;a[1]++){
			for(a[2]=1;a[2]<=9;a[2]++){
				for(a[3]=1;a[3]<=9;a[3]++){
					if(judge(a)){
						cnt++;
					}
				}
			}
		}
	}
	cout<<cnt<<endl;
	return 0;
}
//14
7.标题:六角填数
如图【1.png】所示六角形中,填入1~12的数字。
使得每条直线上的数字之和都相同。
 图中,已经替你填好了3个数字,请你计算星号位置所代表的数字是多少?

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int main(){
	int count=0;
	int a[9]={2,4,5,6,7,9,10,11,12};
	do{
		int sum1=8+a[0]+a[1]+a[2];
		int sum2=a[0]+1+a[3]+a[5];
		int sum3=1+a[1]+a[4]+a[8];
		int sum4=a[5]+a[6]+a[7]+a[8];
		int sum5=3+a[6]+a[3]+8;
		int sum6=3+a[7]+a[4]+a[2];
		if(sum1==sum2&&sum2==sum3&&sum3==sum4&&sum4==sum5&&sum5==sum6){
			cout<<a[3]<<endl;
			break;
		}
	}while(next_permutation(a,a+9));
	return 0;
}
//10
8.蚂蚁感冒
长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 <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int main(){
	int flag=0,cnt=1,n,a[60];
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	if(a[0]>0){//感冒的蚂蚁向右运动
		for(int i=1;i<n;i++){
			if(a[i]<0&&-a[i]>a[0]){
				flag=1;
				break;
			}
		}
		for(int i=1;i<n;i++){
			if(a[i]>0){
				if(a[i]<a[0]&&flag)
					cnt++;
			}
			else{
				if(-a[i]>a[0])
					cnt++;
			}
		}
	}
	else{//感冒的蚂蚁向左运动
		for(int i=1;i<n;i++){
			if(a[i]>0&&a[i]<-a[0]){
				flag=1;
				break;
			}
		}
		for(int i=1;i<n;i++){
			if(a[i]>0){
				if(a[i]<-a[0])
					cnt++;
			}
			else{
				if(-a[i]>-a[0]&&flag)
					cnt++;
			}
		}
	}
	cout<<cnt<<endl;
	return 0;
}
9.地宫取宝
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

有几点需要注意

1.宝贝的价值可能为零,如果不处理,当开始遇到的为价值为零的宝贝,那么就不能取了,所以读取数据时让所有宝贝的价值均加1

2.小明只能向下和向右走,因此小明是无法在当前搜索路径中回到已经走过的位置的,所以不用标记也可以(如法二,法三)

法一:普通搜索,(i.j)表示要去处理的位置

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define MOD 1000000007
int n,m,k;
long long int dp[60][60][15][15];
int map[60][60];
int Next[2][2]={{0,1},{1,0}};
void dfs(int x,int y,int cnt,int Max){
	if(dp[x][y][cnt][Max]!=-1){
		return;
	}
	dp[x][y][cnt][Max]=0;
	if(x==n&&y==m){
		if(cnt==k||(cnt==k-1&&map[n][m]>Max)){
			dp[x][y][cnt][Max]=1;
		}
	}
	int xnext,ynext;
	for(int i=0;i<2;i++){
		xnext=x+Next[i][0];
		ynext=y+Next[i][1];
		if(xnext>=1&&xnext<=n&&ynext>=1&&ynext<=m){
			if(map[x][y]>Max){
				dfs(xnext,ynext,cnt+1,map[x][y]);
				dp[x][y][cnt][Max]+=dp[xnext][ynext][cnt+1][map[x][y]];
				dp[x][y][cnt][Max]%=MOD;
			}
			dfs(xnext,ynext,cnt,Max);
			dp[x][y][cnt][Max]+=dp[xnext][ynext][cnt][Max];
			dp[x][y][cnt][Max]%=MOD;
		}
	}
}
int main(){
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>map[i][j];
			map[i][j]++;
		}
	}
	memset(dp,-1,sizeof(dp));
	dfs(1,1,0,0);
	printf("%lld\n",dp[1][1][0][0]%MOD);
	return 0;
}
法二:可以想到,在搜索过程中有些状态是重复的,因此可采用记忆化搜索,存储已经搜索过的状态

dp[i][j][w][t]:表示当前在(i,j)位置上要去处理,已经取到w个宝贝,取到宝贝的最大价值为t 时到达终点的最终方案数

具体搜索方式仍为法一中的,(i,j)为要处理的点

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define MOD 1000000007
int n,m,k;
long long int dp[60][60][15][15];
int map[60][60];
int Next[2][2]={{0,1},{1,0}};
void dfs(int x,int y,int cnt,int Max){
	if(dp[x][y][cnt][Max]!=-1){
		return;
	}
	dp[x][y][cnt][Max]=0;
	if(x==n&&y==m){
		if(cnt==k||(cnt==k-1&&map[n][m]>Max)){
			dp[x][y][cnt][Max]=1;
		}
	}
	int xnext,ynext;
	for(int i=0;i<2;i++){
		xnext=x+Next[i][0];
		ynext=y+Next[i][1];
		if(xnext>=1&&xnext<=n&&ynext>=1&&ynext<=m){
			if(map[x][y]>Max){
				dfs(xnext,ynext,cnt+1,map[x][y]);
				dp[x][y][cnt][Max]+=dp[xnext][ynext][cnt+1][map[x][y]];
				dp[x][y][cnt][Max]%=MOD;
			}
			dfs(xnext,ynext,cnt,Max);
			dp[x][y][cnt][Max]+=dp[xnext][ynext][cnt][Max];
			dp[x][y][cnt][Max]%=MOD;
		}
	}
}
int main(){
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>map[i][j];
			map[i][j]++;
		}
	}
	memset(dp,-1,sizeof(dp));
	dfs(1,1,0,0);
	printf("%lld\n",dp[1][1][0][0]%MOD);
	return 0;
}


法三:也为记忆化搜索,但具体思路有差异。法二更偏向于搜索,因为考虑时更多考虑(i,j)为当前要处理的点,另两个量是当前位置的附属状态。而法三更偏向于动态规划,四维数组的四个分量共同组成了当前的搜索状态->在(i,j)位置上,已经取到w个宝贝,取到宝贝的最大价值为t ,不管当前搜索路径此位置处没处理过,不管有没有从当前位置取过宝贝

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define MOD 1000000007
int n,m,k;
long long int dp[60][60][15][15];
int map[60][60];
int Next[2][2]={{0,1},{1,0}};
void dfs(int x,int y,int cnt,int Max){
	if(dp[x][y][cnt][Max]!=-1){
		return;
	}
	dp[x][y][cnt][Max]=0;
	if(x==n&&y==m&&cnt==k){
		dp[x][y][cnt][Max]=1;
	}
	if(map[x][y]>Max&&cnt<k){
		dfs(x,y,cnt+1,map[x][y]);
		dp[x][y][cnt][Max]+=dp[x][y][cnt+1][map[x][y]];
		dp[x][y][cnt][Max]%=MOD;
	}
	if(x<n){
		dfs(x+1,y,cnt,Max);
		dp[x][y][cnt][Max]+=dp[x+1][y][cnt][Max];;
		dp[x][y][cnt][Max]%=MOD;
	}
	if(y<m){
		dfs(x,y+1,cnt,Max);
		dp[x][y][cnt][Max]+=dp[x][y+1][cnt][Max];
		dp[x][y][cnt][Max]%=MOD;
	}
}
int main(){
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>map[i][j];
			map[i][j]++;
		}
	}
	memset(dp,-1,sizeof(dp));
	dfs(1,1,0,0);
	printf("%lld\n",dp[1][1][0][0]%MOD);
	return 0;
}
10.小朋友排队
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。

//考察树状数组的一个应用:求逆序数  有一个结论:对于一个序列,想得到按大小排好的序列,每个位置的数交换的次数等于该数产生的逆序数

//每个小盆友交换的次数,就是每个数左边更大右边更小的数的个数
//因为小朋友身高可出现重复,而且而且用到的是左边更大右边更小,所以树状数组表示 管辖范围内 早出现的小于当前数的数 的数量
//注意小朋友身高可为负
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int a[1000010],b[1000010],re[100010],num[100010];
long long unhappy[1000010];
int lowBit(int x){
	return x&(-x);
}
int get(int a[],int p){
	int re=0;
	for(;p>=1;p-=lowBit(p)){
		re+=a[p];
	}
	return re;
}
void update(int a[],int p){
	for(;p<1000010;p+=lowBit(p)){
		a[p]++;
	}
}
int main(){
	int n;
	long long ans=0;
	for(int i=1;i<1000010;i++){
		unhappy[i]=unhappy[i-1]+i;
	}
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&num[i]);
		//从比当前数大的第一个数开始更新
		update(a,num[i]+1);
		if(i!=1){//第一个位置不用记录左边的数比该位置的数小的数量
			re[i]=i-get(a,num[i]+1);//此处用num[i]+1是因为 想要得到左边比自己大的,所以要去掉左边小于等于当前位置数的数(包括自己)
		}
	}
	for(int i=n;i>=1;i--){
		update(b,num[i]+1);
		ans+=unhappy[re[i]+get(b,num[i])];
	}
	printf("%lld\n",ans);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值