第五屆藍橋杯省賽 C/C++真題混搭題解

1.標題:武功祕籍


    小明到X山洞探險,撿到一本有破損的武功祕籍(2000多頁!當然是僞造的)。他注意到:書的第10頁和第11頁在同一張紙上,但第11頁和第12頁不在同一張紙上。
    小明只想練習該書的第81頁到第92頁的武功,又不想帶着整本書。請問他至少要撕下多少張紙帶走?
這是個整數,請通過瀏覽器提交該數字,不要填寫任何多餘的內容。

 

答案:7
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int main() {
	int cnt;
	cnt=0;
	for(int i=10; i<=92; i+=2) {
		cnt++;
		cout<<i<<' '<<i+1<<' '<<cnt<<endl;
	}
	return 0;
}

 

2.標題:等額本金

    小明從銀行貸款3萬元。約定分24個月,以等額本金方式還款。
    這種還款方式就是把貸款額度等分到24個月。每個月除了要還固定的本金外,還要還貸款餘額在一個月中產生的利息。
    假設月利率是:0.005,即:千分之五。那麼,
    第一個月,小明要還本金 1250, 還要還利息:30000 * 0.005,總計 1400.00
    第二個月,本金仍然要還 1250, 但利息爲:(30000-1250) * 0.005 總計 1393.75
    請問:小明在第15個月,應該還款多少(本金和利息的總和)?

    請把答案金額四捨五入後,保留兩位小數。注意:32.5,一定要寫爲:32.50
    通過瀏覽器提交答案,這是一個含有小數點和兩位小數的浮點數字。不要寫多餘內容(例如:多寫了“元”或添加說明文字)
 

答案:1312.50
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int main() {
	double li,ben=1250;
	for(int i=0;i<24;i++){
		li=0.005*(30000-ben*i);
		printf("%d %.2f\n",i+1,li+ben);
	}
	return 0;
}

 

3.標題:猜年齡

    小明帶兩個妹妹參加元宵燈會。別人問她們多大了,她們調皮地說:“我們倆的年齡之積是年齡之和的6倍”。小明又補充說:“她們可不是雙胞胎,年齡差肯定也不超過8歲啊。”
    請你寫出:小明的較小的妹妹的年齡。

注意: 只寫一個人的年齡數字,請通過瀏覽器提交答案。不要書寫任何多餘的內容。

 

答案:10
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int main() {
	for(int i=1;i<=100;i++){
		for(int j=1;j<=100;j++){
			if(i!=j&&abs(i-j)<=8&&i*j==6*(i+j)) cout<<i<<' '<<j<<endl;
		}
	}
	return 0;
}

 

4.標題:大衍數列


    中國古代文獻中,曾記載過“大衍數列”, 主要用於解釋中國傳統文化中的太極衍生原理。
    它的前幾項是:0、2、4、8、12、18、24、32、40、50 ...
    其規律是:對偶數項,是序號平方再除2,奇數項,是序號平方減1再除2。
    以下的代碼打印出了大衍數列的前 100 項。

int main()
{
    int i;
    for(i=1; i<100; i++){
        if(i%2==0) //填空
            printf("%d ", i*i/2);
        else
            printf("%d ", (i*i-1)/2);
    }
    printf("\n");
}

    請填寫劃線部分缺失的代碼。通過瀏覽器提交答案。
注意:不要填寫題面已有的內容,也不要填寫任何說明、解釋文字。

 

5.標題:錦標賽


   如果要在n個數據中挑選出第一大和第二大的數據(要求輸出數據所在位置和值),使用什麼方法比較的次數最少?我們可以從體育錦標賽中受到啓發。
   如圖【1.png】所示,8個選手的錦標賽,先兩兩捉對比拼,淘汰一半。優勝者再兩兩比拼...直到決出第一名。
   第一名輸出後,只要對黃色標示的位置重新比賽即可。
   下面的代碼實現了這個算法(假設數據中沒有相同值)。
   代碼中需要用一個數組來表示圖中的樹(注意,這是個滿二叉樹,不足需要補齊)。它不是存儲數據本身,而是存儲了數據的下標。   
   第一個數據輸出後,它所在的位置被標識爲-1

 

//本題思路類似於間接尋址的堆排序,輸出第一大和第二大的數,即原數據不動,用各個數據的 index 進行堆排序,對於元素數據類型較大的情況下,用 index 進行間接尋址排序的話,可以減少很多數據的搬移,效率相對的要高。這裏是用 b[]記錄元素的index , a[]記錄原始數據。

//重新決出k號位置,v爲已輸出值 
void pk(int* a, int* b, int n, int k, int v)
{
    int k1 = k*2 + 1;
    int k2 = k1 + 1;
    
    if(k1>=n || k2>=n){
        b[k] = -1;
        return;
    }
    
    if(b[k1]==v) 
        pk(a,b,n,k1,v);
    else
        pk(a,b,n,k2,v);
    
    //重新比較
    if(b[k1]<0){
        if(b[k2]>=0)
            b[k] = b[k2]; 
        else
            b[k] = -1;
        return;
    }
    
    if(b[k2]<0){
        if(b[k1]>=0)
            b[k] = b[k1]; 
        else
            b[k] = -1;
        return;
    }
    
    if(a[b[k1]]>a[b[k2]]) //填空         //比較b[k1]與b[k2]所對應的值誰更大
        b[k] = b[k1];
    else
        b[k] = b[k2];
}

//對a中數據,輸出最大,次大元素位置和值 
void f(int* a, int len)
{
    int n = 1;
    while(n<len) n *= 2;            //建立完全二叉樹——下標範圍
    
    int* b = (int*)malloc(sizeof(int*) * (2*n-1));      //分配存放二叉樹的數組空間
    int i;
    for(i=0; i<n; i++){ 
        if(i<len) 
            b[n-1+i] = i;                 
        else
            b[n-1+i] = -1;
    }
    
    //從最後一個向前處理                   //建大頂堆
    for(i=2*n-1-1; i>0; i-=2){           
        if(b[i]<0){
            if(b[i-1]>=0)
                b[(i-1)/2] = b[i-1]; 
            else
                b[(i-1)/2] = -1;
        }
        else{
            if(a[b[i]]>a[b[i-1]])
                b[(i-1)/2] = b[i];
            else
                b[(i-1)/2] = b[i-1];
        }
    }
    
    //輸出樹根
    printf("%d : %d\n", b[0], a[b[0]]);      //輸出分別爲最大元素下標、最大元素的值
    
    //值等於根元素的需要重新pk
    pk(a,b,2*n-1,0,b[0]);
    
    //再次輸出樹根
    printf("%d : %d\n", b[0], a[b[0]]);      //輸出分別爲次大元素下標、次大元素的值
    
    free(b);
}


int main()
{
    int a[] = {54,55,18,16,122,17,30,9,58};
    f(a,9);    
}

    請仔細分析流程,填寫缺失的代碼。
    通過瀏覽器提交答案,只填寫缺失的代碼,不要填寫已有代碼或其它說明語句等。

 

 6.標題:猜字母


    把abcd...s共19個字母組成的序列重複拼接106次,得到長度爲2014的串。
    接下來刪除第1個字母(即開頭的字母a),以及第3個,第5個等所有奇數位置的字母。
    得到的新串再進行刪除奇數位置字母的動作。如此下去,最後只剩下一個字母,請寫出該字母。
答案是一個小寫字母,請通過瀏覽器提交答案。不要填寫任何多餘的內容。

 

答案:q
#include<iostream>
#include<string>
#include<cmath>
using namespace std;
int main (){
	int n=106;
	string str,s="abcdefghijklmnopqrs",tmp;
	while(n--) str+=s;
	while(str.size()!=1){
		tmp="";
		for(int i=1;i<str.size();i+=2)
			tmp+=str[i];      //str.replace(i,1," ");  (錯誤,因爲size實時更新!)
				str=tmp;
	}
	cout<<str;
	return 0;
}

 

7.標題:撲克序列

    A A 2 2 3 3 4 4, 一共4對撲克牌。請你把它們排成一行。
    要求:兩個A中間有1張牌,兩個2之間有2張牌,兩個3之間有3張牌,兩個4之間有4張牌。
    請填寫出所有符合要求的排列中,字典序最小的那個。

例如:22AA3344 比 A2A23344 字典序小。當然,它們都不是滿足要求的答案。
請通過瀏覽器提交答案。“A”一定不要用小寫字母a,也不要用“1”代替。字符間一定不要留空格。

 

//2342A3A4
//'2'、'3'、'4'、'A'的ASC碼依次爲50,51,52,65
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int num[5];
bool judge(int a[]){
	memset(num,-1,sizeof num);
	for(int i=0;i<8;i++){
		if(num[a[i]]!=-1)                     //tip!
			num[a[i]]=abs(i-num[a[i]]-1);
		else 
			num[a[i]]=i;
	}
	for(int i=1;i<=4;i++)
		if(num[i]!=i) return false;
	return true;
}
int main(){
	int a[8]={2,2,3,3,4,4,1,1};  //在這裏用1代替A
	do{
		if(judge(a)){
			for(int i=0;i<8;i++)
				cout<<a[i];
			cout<<endl;
		}
	}while(next_permutation(a,a+8));
}

 

8.標題:分糖果

    有n個小朋友圍坐成一圈。老師給每個小朋友隨機發偶數個糖果,然後進行下面的遊戲:
    每個小朋友都把自己的糖果分一半給左手邊的孩子。
    一輪分糖後,擁有奇數顆糖的孩子由老師補給1個糖果,從而變成偶數。
    反覆進行這個遊戲,直到所有小朋友的糖果數都相同爲止。
    你的任務是預測在已知的初始糖果情形下,老師一共需要補發多少個糖果。


【格式要求】
    程序首先讀入一個整數N(2<N<100),表示小朋友的人數。
    接着是一行用空格分開的N個偶數(每個偶數不大於1000,不小於2)
    要求程序輸出一個整數,表示老師需要補發的糖果數。


例如:輸入
3
2 2 4
程序應該輸出:
4

資源約定:
峯值內存消耗 < 256M
CPU消耗  < 1000ms

請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入...” 的多餘內容。
所有代碼放在同一個源文件中,調試通過後,拷貝提交該源碼。


注意: main函數需要返回0
注意: 只使用ANSI C/ANSI C++ 標準,不要調用依賴於編譯環境或操作系統的特殊函數。
注意: 所有依賴的函數必須明確地在源文件中 #include <xxx>, 不能通過工程設置而省略常用頭文件。


提交時,注意選擇所期望的編譯器類型。
 

#include<iostream>
#include<string>
#include<cmath>
using namespace std;
int n;
int a[105];
int judge(){
	int x=a[1];
	for(int i=2;i<=n;i++)
		if(a[i]!=x) return 0;
	return 1;
}
int main () {
	int ans=0,tmp;
	cin>>n;
	for(int i=1; i<=n; i++)
		scanf("%d",&a[i]);
	while(judge()==0) {
		tmp=a[1]/2;                                   //防數據遺失
		for(int i=1;i<n;i++){ 
			a[i]=a[i]/2+a[i+1]/2;
			if(a[i]%2){
				a[i]++;
				ans++;
			}
		}
		a[n]=a[n]/2+tmp; 
		if(a[n]%2){
			a[n]++;
			ans++;
		}
	}
	cout<<ans;
	return 0;
}

 


9.標題:斐波那契

    斐波那契數列大家都非常熟悉。它的定義是:

    f(x) = 1                    .... (x=1,2)
    f(x) = f(x-1) + f(x-2)      .... (x>2)

    對於給定的整數 n 和 m,我們希望求出:
    f(1) + f(2) + ... + f(n)  的值。但這個值可能非常大,所以我們把它對 f(m) 取模。
    公式參見【圖1.png】 

    但這個數字依然很大,所以需要再對 p 求模。

【數據格式】
輸入爲一行用空格分開的整數 n m p (0 < n, m, p < 10^18)
輸出爲1個整數

例如,如果輸入:
2 3 5
程序應該輸出:
0

再例如,輸入:
15 11 29
程序應該輸出:
25

資源約定:
峯值內存消耗 < 256M
CPU消耗  < 1000ms

請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入...” 的多餘內容。
所有代碼放在同一個源文件中,調試通過後,拷貝提交該源碼。

注意: main函數需要返回0
注意: 只使用ANSI C/ANSI C++ 標準,不要調用依賴於編譯環境或操作系統的特殊函數。
注意: 所有依賴的函數必須明確地在源文件中 #include <xxx>, 不能通過工程設置而省略常用頭文件。

提交時,注意選擇所期望的編譯器類型。


 

 

四十分代碼,待更新……
#include<iostream>
#include<vector>
#define ll long long
using namespace std;
vector<long long> v;
ll n,m,p,tmp,sum=0;
void init() {
	tmp=max(n,m);
	v.push_back(0);
	v.push_back(1);
	v.push_back(1);
	for(int i=3; i<=tmp; i++)
		v.push_back(v[i-1]+v[i-2]);
}
int main() {
	cin>>n>>m>>p;
	init();
	for(int i=1; i<=n; i++) {
		sum=(sum+v[i])%v[m];
	}
	sum=sum%p;
	cout<<sum<<endl;
	return 0;
}

 

 

10.標題:矩陣翻硬幣

    小明先把硬幣擺成了一個 n 行 m 列的矩陣。
    隨後,小明對每一個硬幣分別進行一次 Q 操作。
    對第x行第y列的硬幣進行 Q 操作的定義:將所有第 i*x 行,第 j*y 列的硬幣進行翻轉。
    其中i和j爲任意使操作可行的正整數,行號和列號都是從1開始。
    當小明對所有硬幣都進行了一次 Q 操作後,他發現了一個奇蹟——所有硬幣均爲正面朝上。
    小明想知道最開始有多少枚硬幣是反面朝上的。於是,他向他的好朋友小M尋求幫助。
    聰明的小M告訴小明,只需要對所有硬幣再進行一次Q操作,即可恢復到最開始的狀態。然而小明很懶,不願意照做。於是小明希望你給出他更好的方法。幫他計算出答案。

【數據格式】
    輸入數據包含一行,兩個正整數 n m,含義見題目描述。
    輸出一個正整數,表示最開始有多少枚硬幣是反面朝上的。

【樣例輸入】
2 3

【樣例輸出】
1

【數據規模】
對於10%的數據,n、m <= 10^3;
對於20%的數據,n、m <= 10^7;
對於40%的數據,n、m <= 10^15;
對於10%的數據,n、m <= 10^1000(10的1000次方)。

資源約定:
峯值內存消耗 < 256M
CPU消耗  < 1000ms

請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入...” 的多餘內容。
所有代碼放在同一個源文件中,調試通過後,拷貝提交該源碼。

注意: main函數需要返回0
注意: 只使用ANSI C/ANSI C++ 標準,不要調用依賴於編譯環境或操作系統的特殊函數。
注意: 所有依賴的函數必須明確地在源文件中 #include <xxx>, 不能通過工程設置而省略常用頭文件。

提交時,注意選擇所期望的編譯器類型。

 

以下解析摘自網絡,後文附有原文鏈接 

1.很容易得出,如果一枚硬幣被翻了奇數次,那麼它原來的狀態肯定是反面朝上,所以,我們要找的就是被翻了奇數次的硬幣
2. 根據Q操作定義,我們舉個例子,對於(2,3)這個點只有在(1,1)(1,3)(2,1)(2,3)這四個點進行Q操作時才翻轉,一共翻轉了4次,通過多個例子總結不難看出,(x,y)點再所有點進行完Q操作後翻轉的次數爲a*b次,其中a爲x的約數,b爲y的約數。因此若想要這個硬幣被翻奇數次,a和b必須都得是奇數,即x和y都有奇數個約數。


先普及一個數論知識完全平方數有奇數個約數
那個這個問題就轉化成了——輸入兩個數n,m,設小於等於n的完全平方數的個數是a,小於等於m的完全平方數的個數是b,求a*b。那麼怎麼求小於等於n和m完全平方數的個數呢?
再普及一個知識:

那麼這個題目有轉化成了——輸入兩個數n,m,求[sqrt(n)]*[sqrt(m)]。
這樣題目就簡單很多了,只要解決兩個問題就可以
1.大數的乘法
2.大數的平方根取整


問題1解決方法:
先弄兩個代表數值的字符串s1,s2,將s1的每一位與s2相乘,存到一個整形數組裏
舉個例子s1=“123”,s2=“89”
123*89

規律:將s1[i]*s2[j]存入num[i+j+1]中,因爲num[0]要存最後進位存完後,對num數組進行倒着計算,比如27實際上就是123*89計算中3*9=27,即把7留下2進上去

最後結果就是10947


問題2解決方法:

這個開方方法不是我想出來的,是參照了大神的一個方法

假如一個數有偶數位n,那麼這個數的方根有n/2位;如果n爲奇數,那麼方根爲(n+1)/2位

然後,讓我們實際看一個例子,我們假設這個數就是1200

1.很明顯,它有4位,所以它的方根有2位,然後,我們通過下面的方法來枚舉出它的整數根

00*00=0<1200

10*10=100<1200

20*20=400<1200

 30*30=900<1200

40*40=1600>1200

所以,這個根的十位就是3,然後,再枚舉個位

 31*31=961<1200

32*32=1024<1200

33*33=1089<1200

34*34=1156<1200

所以,這個根就是34,因爲平方增長的速度還是比較快的,所以速度沒有太大問題。爲了提高速度,我們可以優化比較函數:

還拿上面的例子來說

 

30*30=900<1200

40*40=1600>1200
這兩個式子,一般來說,我們應該先計算3*3=9,然後在9後面添2個0再與1200比較,但由於數據量很大,添零也會消耗時間

於是我們可以計算需要加的0的數量然後用下面的方法直接比較

1.如果第i個數的平方的位數加上需要添加的零的個數之後位數與原數不相等,那麼位數大的數值大

2.如果位數相等就沒必要再添零,直接進行字符串比較即可

例如:

 

30*30=900<1200

40*40=1600>1200

十位是3的時候 3*3=9是1位填上兩個零後位數位3位小於1200的4位所以900<1200

十位是4的時候 4*4=16是兩位,添上兩個零後位數爲4位等於1200的四位,所以只需比較字符串16與1200的大小

很明顯在字符串中16>1200,所以1600>1200

那麼添加零的個數怎麼算呢?

假設一個數的平方根取整的位數爲n,從前往後算目前計算到了第i位,則需要添加2*(n-1-i)個零

例如:1200 平方根取整有2位,目前算到了第0位(從0開始計數)即30*30(我們算的是3*3),需要加2*(2-1-0)=2個零
原文:https://blog.csdn.net/reidsc/article/details/64924051 

#include <iostream>
#include <algorithm>
using namespace std;
string StrMul(string s1,string s2) { //大數乘法
	string ans;
	int num[500]= {0},i,j;
	for(i=0; i<s1.length(); i++) //s計算存到num中
		for(j=0; j<s2.length(); j++)
			num[i+j+1]+=(s1[i]-'0')*(s2[j]-'0');
	for(i=s1.length()+s2.length()-1; i>0; i--) //num的處理
		if(num[i]>=10)		{
			num[i-1]+=num[i]/10;
			num[i]%=10;
		}
	for(int i=0; i<=s1.length()+s2.length()-1; i++) //將num數存到ans字串中,注意進位爲0的情況
		if(!i&&num[i]||i)
			ans.push_back(num[i]+'0');
	return ans;
}
bool StrCmp(string s1,string s2,int pos) { //比較兩字符串大小,pos代表應該在s1後面填幾個零

	if(s1.length()+pos!=s2.length())//如果s1位數不等於s2,
		return s1.length()+pos>s2.length();
	else//位數相等
		return s1>s2;
}
string SqrtStr(string s) { //大數平方根取整
	int len;
	string ans;
	if(s.length()%2==0)//長度爲偶數
		len=s.length()/2;
	else
		len=s.length()/2+1;
	for(int i=0; i<len; i++) { //一位一位的循環
		ans.push_back('0');
		for(int j=0; j<=9; j++)		{
			if(StrCmp(StrMul(ans,ans),s,2*(len-1-i)))//需要添加0的個數是2*(len-1-i)解析見上面
				break;
			ans[i]++;
		}
		ans[i]--;
	}
	return ans;
}
int main() {
	string s1,s2;
	cin>>s1>>s2;
	cout<<StrMul(SqrtStr(s1),SqrtStr(s2))<<endl;
	return 0;
}


 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章