2015年蓝桥杯六届省赛大学B组真题

2015年蓝桥杯六届省赛大学B组真题

1.奖券数目

 
#include <iostream>
using namespace std;
int ans;
bool check(int i){
  while(i){
    if(i%10==4)return false;
    i/=10;
  }
  return true;
}
int main()
{
  int ans=0;
  for(int i=10000;i<=99999;i++){
    if(check(i)){
ans++;
    }
  }
  cout<<ans;
  // 请在此输入您的代码
  return 0;
}

2.星系炸弹

#include <bits/stdc++.h>
using namespace std;
int main(){
    int mon[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
    int y=2014,d=9,m=11,w;
    for(w=0;w<1000;w++){
//注意:二月份闰年平年处理
      if((y%4==0&&y%100!=0)||(y%400==0)) mon[2]=29;
      else  mon[2]=28;
天数更新一天d++,d表示当月的哪一日!
      d++;
//这个月满了换成下一个月的第一天
      if(d>mon[m]){
        d=1;
        m++;
      }
//这一年满了12个月满了,换为新的一年第一个月。
      if(m>12){
        y++;
        m=1;
      }
    }
    printf("%04d-%02d-%02d",y,m,d);//数位表示方法:%6d表示这个整数显示长度为6位不够就左边补空格
//%02d表示这个整数显示长度为2不够位数左边补0
    return 0;
}
考点是:日期计算
用一个日期数组存放每一个月的天数
然后注意年月日表示

3.三羊献瑞

枚举

#include <iostream>
using namespace std;
int main()
{
  // 请在此输入您的代码
int a1,a2,a3,a4,a5,a6,a7,a8;
for(int i=0;i<=9;i++){//辉
  for(int j=0;j<=9;j++){//生
    if(i==j)continue;
    for(int p=0;p<=9;p++){//瑞
      if(p==i||p==j)continue;
      for(int q=1;q<=9;q++){//祥
        if(q==i||q==j||q==p)continue;
        for(int m=1;m<=9;m++){//三
          if(m==p||m==i||m==j||m==q)continue;
for(int n=0;n<=9;n++){//羊
  if(n==m||n==p||n==q||n==i||n==j)continue;
  for(int x=0;x<=9;x++){//献
if(x==m||x==p||x==q||x==i||x==j||x==n)continue;
for(int h=0;h<=9;h++){//气
if(h==m||h==p||h==q||h==i||h==j||h==n||h==x)continue;
if(q*1000+100*p+10*j+i+1000*m+100*n+10*x+p==10000*m+1000*n+100*j+10*p+h){
  cout<<1000*m+100*n+10*x+p;
  return 0;
}
}
  }
}
        }
      }
    }
  }
}
  return 0;
}
 
 
 

4.格子中输出

第四题:格子中输出
StringInGrid函数会在一个指定大小的格子中打印指定的字符串。
要求字符串在水平、垂直两个方向上都居中。
如果字符串太长,就截断。
如果不能恰好居中,可以稍稍偏左或者偏上一点。
 
下面的程序实现这个逻辑,请填写划线部分缺少的代码。
题目
#include <stdio.h>
#include <string.h>
 
void StringInGrid(int width, int height, const char* s)
{
	int i,k;
	char buf[1000];
	strcpy(buf, s);
	if(strlen(s)>width-2) buf[width-2]=0;
	
	printf("+");
	for(i=0;i<width-2;i++) printf("-");
	printf("+\n");
	
	for(k=1; k<(height-1)/2;k++){
		printf("|");
		for(i=0;i<width-2;i++) printf(" ");
		printf("|\n");
	}
	
	printf("|");
	
	printf("%*s%s%*s",_____________________________________________);  //填空
	          
	printf("|\n");
	
	for(k=(height-1)/2+1; k<height-1; k++){
		printf("|");
		for(i=0;i<width-2;i++) printf(" ");
		printf("|\n");
	}	
	
	printf("+");
	for(i=0;i<width-2;i++) printf("-");
	printf("+\n");	
}
 
int main()
{
	StringInGrid(20,6,"abcd1234");
	return 0;
}
答案
#include <stdio.h>
#include <string.h>
 
void StringInGrid(int width, int height, const char* s)
{
    int i,k;
    char buf[1000];
    strcpy(buf, s);
    if(strlen(s)>width-2) buf[width-2]=0;
    
    printf("+");
    for(i=0;i<width-2;i++) printf("-");
    printf("+\n");
    
    for(k=1; k<(height-1)/2;k++){
        printf("|");
        for(i=0;i<width-2;i++) printf(" ");
        printf("|\n");
    }
    
    printf("|");
    
    printf("%*s%s%*s",(width-strlen(buf)-2)/2," ",buf,(width-strlen(buf)-2)/2);  //填空答案
              
    printf("|\n");
    
    for(k=(height-1)/2+1; k<height-1; k++){
        printf("|");
        for(i=0;i<width-2;i++) printf(" ");
        printf("|\n");
    }    
    
    printf("+");
    for(i=0;i<width-2;i++) printf("-");
    printf("+\n");    
}
 
int main()
{
    StringInGrid(20,6,"abcd1234");
    return 0;
}

考点:

  printf("%*s%s%*s",(width-strlen(buf)-2)/2," ",buf,(width-strlen(buf)-2)/2," ");  //填空答案

"%*s",数字,字符串

表示从第几个位置开始输出字符串,第一个位置为1号位!

 

 

5.九数组分数

1,2,3...9 这九个数字组成一个分数,其值恰好为1/3,如何组法?
 
下面的程序实现了该功能,请填写划线部分缺失的代码。
 
#include <stdio.h>
 
void test(int x[])
{
    int a = x[0]*1000 + x[1]*100 + x[2]*10 + x[3];
    int b = x[4]*10000 + x[5]*1000 + x[6]*100 + x[7]*10 + x[8];
    
    if(a*3==b) printf("%d / %d\n", a, b);
}
 
void f(int x[], int k)
{
    int i,t;
    if(k>=9){
        test(x);
        return;
    }
    
    for(i=k; i<9; i++){
        {t=x[k]; x[k]=x[i]; x[i]=t;}
        f(x,k+1);
       t=x[k]; x[k]=x[i]; x[i]=t;// 填空处
    }
}
    
int main()
{
    int x[] = {1,2,3,4,5,6,7,8,9};
    f(x,0);    
    return 0;
}
 
考核思路:
dfs算法
解析:对每一个位置进行全排列的dfs搜索
for(i=k; i<9; i++){
 
        {t=x[k]; x[k]=x[i]; x[i]=t;}//1.改变
        f(x,k+1);//dfs下一种情况
       t=x[k]; x[k]=x[i]; x[i]=t;// 填空处,恢复现场。
    }
}
 
 

6.移动距离

#include <bits/stdc++.h>
using namespace std;
int main()
{
  int w,m,n;
 cin>>w>>m>>n; 
  int h=0;
  int z=0;
  int x1,y1;
  int x2,y2;
  int sign=1;
  for(int i=1;i<=10000;i++){
//考察走楼梯,语法
z=z+sign;
if(z>w){
  z=w;
  h++;
  sign=-1;
}
if(z<1){
  z=1;
  sign=1;
  h++;
}
if(i==m){
  x1=h;
  y1=z;
}
if(i==n){
  x2=h;
  y2=z;
}
  }
    //注意相减可能<0会抵消,所以abs绝对值
  cout<<abs(x2-x1)+abs(y2-y1);
  // 请在此输入您的代码
  return 0;
}

7.加法变乘法

计算思路:可以求出满足条件的乘号位置,方法是遍历这两个乘号可能的位置

#include<bits/stdc++.h>
using namespace std;
int main(){
for(int i=1;i<=48;i++) {
for(int j=i+2;j<=48;j++){
if(1225-(i+i+1)-(j+j+1)+i*(i+1)+j*(j+1)==2015){
cout<<"i= "<<i<<",j= "<<j<<endl;
}
}
}
return 0;
}

答案:16(也就是不等于10的i)

 

8.牌型种数

思路:暴力,将13种牌型都枚举

十三种排序:A,2-10,J,Q,K

#include<bits/stdc++.h>
using namespace std;
int main(){
long long int cnt=0;
for(int a=0;a<=4;a++){//每张牌最多取4次(因为4色)
    for(int b=0;b<=4;b++){
        for(int c=0;c<=4;c++){
            for(int d=0;d<=4;d++){
                for(int e=0;e<=4;e++){
                    for(int f=0;f<=4;f++){
                        for(int g=0;g<=4;g++){
                            for(int h=0;h<=4;h++){
                                for(int i=0;i<=4;i++){
                                    for(int j=0;j<=4;j++){
                                        for(int k=0;k<=4;k++){
                                            for(int l=0;l<=4;l++){
                                                for(int m=0;m<=4;m++){
                                                    if(a+b+c+d+e+f+g+h+i+j+k+l+m==13){
                                                        cnt++;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
cout<<cnt;
}

提交答案:

#include<bits/stdc++.h>
using namespace std;
int main(){
    cout<<3598180;
    return 0;
}

9.生命之树(最大权值子树和)

注意遍历相邻点时父亲结点不要再重新遍历。

算法:树状dp+dfs+邻接表存图

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int N=1e5+6;
const int M=2*N;
int e[N];//存放每个点的值
int idx;//当前已经存在几个点
int h[N] ;//存放头结点
int ne[M] ;//存放当前结点指向的下一节点
int w[N];//每个点的分(权值)
int n;
ll f[N];//f[i]表示以i为根节点的连通块权值和的最大值(dp)
void add(int a,int b){//邻接表存图
//建立a->b这条边,思想是将b结点放入a的邻接单链表里
//1.存插入值
e[idx]=b;
//2.连接
ne[idx] =h[a];
h[a]=idx++;
}
//dfs计算树形dp其实是递归
void dfs(int a,int father) {
f[a]=w[a];
for(int i=h[a];i!=-1;i=ne[i]){
int j=e[i];
if(j!=father){
//
dfs(j,a);
f[a]+=max(0ll,f[j]);
}
}
}
int main(){
memset(h,-1,sizeof(h)) ;
cin>>n;
for(int i=1;i<=n;i++){
//点从1号开始标
cin>>w[i] ;
}
//邻接表存图,邻接表就是多个单链表,每个单链表存放与当前点相邻的点
for(int i=0;i<n-1;i++){//对于树,n个结点对应n-1条无向边
int a,b;
cin>>a>>b;
//无向边就要双向存
add(a,b);
add(b,a);
}
dfs(1,-1) ;//dfs计算每个结点为根节点的最大权值子树包括这个根的权值

ll res=0;
for(int i=1;i<=n;i++){
res=max(res,f[i]);//遍历看哪个为根节点的连通块权值和最大
}
cout<<res;
return 0;
}

完整代码

 #include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int N=1e5+6;//结点数
const int M=2*N;
int w[N] ;//每个结点的评分
int h[N] ;//头结点
//因为树是无向图,所以一条边存两遍(a,b)(b,a),所以e[ ],ne[ ]容量开两遍因为存节点下标 
int e[M] ;//存放当前点的下标
int ne[M];//next指针,指向下一个节点 
int idx;//当前在第几个点
ll dp[N];//dp[u]表示以u为根的连通块最大权值和,树形dp
int n;//顶点个数 
void add(int a,int b) {
	//添加a->b这条边
	//利用头插法,在a头结点后面插入b 
	e[idx]=b;//第一步存放b点
	//第二步:连接
	ne[idx] =h[a];
	h[a]=idx++;
}
//树状dp,用递归dfs
void dfs(int u,int father){
	dp[u]=w[u];//以u为根的连通块,他包含u这个点作为根 
	for(int i=h[u];i!=-1;i=ne[i]){//遍历u的所有邻结点 
		int j=e[i];
		if(j!=father){//防止重复遍历,所以遍历邻结点但不能是他的父节点 
		dfs(j,u);//递归,下一层
		dp[u]+=max(0ll,dp[j]);//dp[u]以u为根节点最大连通块权值和,所以只加上非负的邻接点	
		}
	}
} 
int main(){
	memset(h,-1,sizeof(h));//初始化头结点链表 
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>w[i];;//每个结点的权值(评分),注意从1号结点开标 
	}
	//邻接表建图,邻接表就是多个单链表,每个单链表是存放与当前结点相连的点 
	for(int i=0;i<n-1;i++){
		//有n个顶点的树拥有n-1条无向边
		int a,b;
		cin>>a>>b;
		//建立边存入邻接表
		add(a,b);
		add(b,a);
	}
	//dfs
	dfs(1,-1);//从第一个结点开始搜,然后他的父节点是-1 
	ll ans=0;
	for(int i=1;i<=n;i++){
		ans=max(ans,dp[i]);
	}
	cout<<ans;
	return 0;
}

核心代码

邻接表存图

 

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int N=1e5+6;//结点数
const int M=2*N;
int w[N] ;//每个结点的评分
int h[N] ;//头结点
//因为树是无向图,所以一条边存两遍(a,b)(b,a),所以e[ ],ne[ ]容量开两遍因为存节点下标 
int e[M] ;//存放当前点的下标
int ne[M];//next指针,指向下一个节点 
int idx;//当前在第几个点
ll dp[N];//dp[u]表示以u为根的连通块最大权值和,树形dp
int n;//顶点个数 
void add(int a,int b) {
	//添加a->b这条边
	//利用头插法,在a头结点后面插入b 
	e[idx]=b;//第一步存放b点
	//第二步:连接
	ne[idx] =h[a];
	h[a]=idx++;
}

int main(){
	memset(h,-1,sizeof(h));//初始化头结点链表 
	
	//邻接表建图,邻接表就是多个单链表,每个单链表是存放与当前结点相连的点 
	for(int i=0;i<n-1;i++){
		//有n个顶点的树拥有n-1条无向边
		int a,b;
		cin>>a>>b;
		//建立边存入邻接表
		add(a,b);
		add(b,a);
	}
	
}

树状dp+dfs

#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int N=1e5+6;//结点数
const int M=2*N;
int w[N] ;//每个结点的评分
int h[N] ;//头结点
//因为树是无向图,所以一条边存两遍(a,b)(b,a),所以e[ ],ne[ ]容量开两遍因为存节点下标 
int e[M] ;//存放当前点的下标
int ne[M];//next指针,指向下一个节点 
int idx;//当前在第几个点
ll dp[N];//dp[u]表示以u为根的连通块最大权值和,树形dp
int n;//顶点个数 

//树状dp,用递归dfs
void dfs(int u,int father){
	dp[u]=w[u];//以u为根的连通块,他包含u这个点作为根 
	for(int i=h[u];i!=-1;i=ne[i]){//遍历u的所有邻结点 
		int j=e[i];
		if(j!=father){//防止重复遍历,所以遍历邻结点但不能是他的父节点 
		dfs(j,u);//递归,下一层
		dp[u]+=max(0ll,dp[j]);//dp[u]以u为根节点最大连通块权值和,所以只加上非负的邻接点	
		}
	}
} 
int main(){
	
	//dfs
	dfs(1,-1);//从第一个结点开始搜,然后他的父节点是-1 
	ll ans=0;
	for(int i=1;i<=n;i++){
		ans=max(ans,dp[i]);
	}
	cout<<ans;
	return 0;
}

 10.垒骰子

分析代码

点数0~5

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 6, mod = 1e9 + 7;

int n, m;
得到这个面对面的点数
int get_op(int x)
{
    if (x >= 3) return x - 3;
    return x + 3;
}
//矩阵乘法
void mul(int c[][N], int a[][N], int b[][N])  // C = A * B
{
     int t[N][N];
    memset(t, 0, sizeof t);
    for (int i = 0; i < N; i ++ )
        for (int j = 0; j < N; j ++ )
            for (int k = 0; k < N; k ++ )
                t[i][j] = (t[i][j] + (LL)a[i][k] * b[k][j]) % mod;
    memcpy(c, t, sizeof t);
}

int main()
{
    cin >> n >> m;//n个骰子,m种限制
矩阵
    int a[N][N];
    for (int i = 0; i < N; i ++ )
        for (int j = 0; j < N; j ++ )
            a[i][j] = 4;
增加限制:
    while (m -- )
    {
        int x, y;
        cin >> x >> y;//因为要把点数化为0开始所以先-1
        x --, y -- ;
        a[x][get_op(y)] = 0;
        a[y][get_op(x)] = 0;
/*分析:例如如果限制是1,2那么如果下面那个骰子的最上面是1,它上面那个骰子最上面不能是5(就是2的对面)。
如果下面那个骰子的最上面是2那么它上面那个骰子最上面不能是5(即1的对面)*/
    }
//f[N][N]的第0行的每一列存放当前最上面不同点数的情况数,其他行全0,目的是将向量化矩阵
    int f[N][N] = {4, 4, 4, 4, 4, 4}; // f[1]
    for (int k = n - 1; k; k >>= 1)  // 快速幂,为求A的n-1次方
    {
        if (k & 1) mul(f, f, a);  // F = F * A
        mul(a, a, a);  // A = A * A
    }
//其实f[0][i]表示当前所有骰子用完后每个点数在最上层的可能数
    int res = 0;
    for (int i = 0; i < N; i ++ ) res = (res + f[0][i]) % mod;

    cout << res << endl;

    return 0;
}
完整代码
#include<bits/stdc++.h>
using namespace std;
const int N=6;
typedef long long int ll;
const int mod=1e9+7;
int a[N][N];//带限制的矩阵,即系数矩阵
int ops(int x){
  if(x>=3)return x-3;
  return x+3;
}
//矩阵乘法
void mul(int c[N][N],int d[N][N],int e[N][N]){//d*e=c
  static int t[N][N];
  memset(t,0,sizeof (t));
  for(int i=0;i<N;i++){
    for(int j=0;j<N;j++){
      for(int k=0;k<N;k++){
t[i][j]=(t[i][j]+(ll)d[i][k]*e[k][j])%mod;
      }
    }
  }
  memcpy(c,t,sizeof(t));
}
int main(){
int n,m;//n个骰子,m种限制
cin>>n>>m;
    //系数矩阵初始化4,就是没限制
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
  a[i][j]=4;
}
}
while(m--){//限制
int x,y;
cin>>x>>y;
x--;y--;//化为0~5点数
//这是下面那个骰子的系数矩阵,如果下面那个骰子上面是x不能转移到上面那个骰子的下面是y的情况也就是上面那个骰子上面是ops(y)
a[x][ops(y)]=0;
a[y][ops(x)]=0;
}
int f[N][N]={4,4,4,4,4,4};//存结果,初始是只有一个骰子那么每个点数做最上面的情况都是4
long long int ans=0;
    
for(int k=n-1;k;k>>=1){//快速幂,目的计算f*a的n-1次方
 if(k&1) mul(f,f,a);//计算a*f,结果乘
mul(a,a,a);//基数变化
}
//最上面行就是答案,即用完所有骰子最上面骰子的上面是各个点数的情况数然后求和
for(int i=0;i<N;i++){
  ans=(ans+f[0][i])%mod;
}
cout<<ans;
return 0;
}
思路解析:
因为要计算不同种类情况数所以用动态规划。
动态规划:
状态表示:
因为本题只和最上层骰子最上面有要求所以可以设置F[i][j]表示有i个骰子且最上面骰子的上面点数是j。
状态转移方程:
如果没有限制
F[i][j]=4*(F[i-1][1]+F[i-1][2]+F[i-1][3]+F[i-1][4]+F[i-1][5]+F[i-1][6]);//乘4的原因是当最上面骰子的上面固定他可以四周旋转所以乘4.
有限制:
那么有的系数可能变为0
例如限制是1,2不能紧挨
那么如果下面那个骰子最上面是1,最上面那个骰子的下面不能是2-->最上面那个骰子的上面不能是2的对面即不能是5
同理
如果下面那个骰子最上面是2,最上面那个骰子的下面不能是1-->最上面那个骰子的上面不能是1的对面即不能是4
所以需要一个求对面的函数
存的时候,因为是按点数0~5存
ops函数
        x                                ops(x)
现在所在面                          对面
0                                            3
1                                            4
2                                            5
3                                            0
4                                            1
5                                            2
发现规律
如果x>=3 ,对面是x-3
如果x<3,对面是x+3
递归
 
 
 
 
快速幂模版
for(int k=n-1;k;k>>=1){//快速幂,目的计算f*a的n-1次方
 if(k&1)乘法
乘方
}
 
 
矩阵乘法(计算d矩阵乘e矩阵并且结果存入c)
void mul(int c[N][N],int d[N][N],int e[N][N]){
   int t[N][N];//暂存数组
  memset(t,0,sizeof (t));
计算思路:
计算第i行j列元素值就是d数组第i行依次乘上e数组第j列
  for(int i=0;i<N;i++){
    for(int j=0;j<N;j++){
      for(int k=0;k<N;k++){
t[i][j]=(t[i][j]+(ll)d[i][k]*e[k][j])%mod;
      }
    }
  }
  memcpy(c,t,sizeof(t));//将t数组内容放入c
}
 
 
 
posted @ 2024-04-04 09:10  Annaprincess  阅读(41)  评论(0)    收藏  举报