2015年藍橋杯六屆省賽大學B組真題

Annaprincess發表於2024-04-04

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",(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種牌型都列舉

#include<bits/stdc++.h>
using namespace std;
int main(){
long long int cnt=0;
for(int a=0;a<=4;a++){
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]){
  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
遞迴
2015年藍橋杯六屆省賽大學B組真題
快速冪模版
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
}

相關文章