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

    xiaoxiao2021-03-25  82

    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; } } } } //112.切面条 一根高筋拉面,中间切一刀,可以得到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; } //144.史丰收速算 史丰收速算法的革命性贡献是:从高位算起,预测进位。不需要九九表,彻底颠覆了传统手算! 速算的核心基础是: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; } //108.蚂蚁感冒 长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; }

    转载请注明原文地址: https://ju.6miu.com/read-32797.html

    最新回复(0)