1. 程式人生 > >C++程式設計課程設計論文

C++程式設計課程設計論文

《c++程式設計》課程設計報告論文班級:數學3班 學號:2018212790報告人姓名:朱泳基 實驗地點:東校區教學樓413完成起止日期: 2019年1月2號到1月5號①簡要題意:有一頭母牛,它每年年初生一頭小母牛。每頭小母牛從第四個年頭開始,每年年初也生一頭小母牛。請程式設計實現在第n年的時候,共有多少頭母牛? 解題思路:這道題問第幾年有幾頭牛,首先看輸入輸出,可以知道當年數小於等於4的時候,第幾年就有幾頭牛,當n大於4的時候,這時候第一年出生的小母牛又可以生小牛了,也就是說要考慮到小牛是否可以生了。每年都有有a(n-1)頭母牛,那麼就要知道這一年出生的母牛有多少。第n-3年有多少頭母牛,到了第n年這些牛都能生小牛了,因此出生數a(n-3。 從而今年的母牛數為a(n)=a(n-1)+a(n-3)。因此得到遞推公式:a(1)=1,a(2)=2,a(3)=3,a(n)=a(n-1)+a(n-3)(n>=4)。解題細節:只要弄清題意,找到規律a(1)=1,a(2)=2,a(3)=3,a(n)=a(n-1)+a(n-3)(n>=4),就行了。原碼:#include int a[60];//開的稍微大一點int main(){ int n; a[1] = 1; a[2] = 2; a[3] = 3; a[4] = 4; for(int i=5;i <= 60;i++) a[i] = a[i-1] + a[i-3]; while(~scanf("%d",&n) && n != 0){//等價於scanf("%d",&n) != EOF printf("%d\n",a[n]); } return 0;o}②簡要題意:有一個長度為n(n<=100)的數列,該數列定義為從2開始的遞增有序偶數,現在要求你按照順序每m個數求出一個平均值,如果最後不足m個,則以實際數量求平均值。程式設計輸出該平均值序列。解題思路:數列:2,4,6,8,10,12,14,16,18,20,22,24 …… 2n每 m 個數求出一個平均值,即求這 m 個數中的“第一個數和最後一個數的平均值”.假設 q = n /m ; r = n % m ;(q = 1,2,3,4 …… ; r = 0,1,2,3 …… ;即數列可分成 q 組餘 r 個數) 則:m 個數一組的時候:第一個數:2 * [ ( q -1) * m + 1 ] 。最後一個數:2 * q * m 平均值:2 * q * m - m + 1最後不足 m 個數的時候:第一個數:2 * [ ( q -1) * m + 1 ] 最後一個數:2 * [ ( q -1) * m + r ] 平均值:2 * q * m - 2 * m + r + 1原碼: #include<stdio.h>int main(){ int n,m,i,sum,num; while(scanf("%d %d",&n,&m)!=EOF) { num=0;sum=0; for(i=2;i<=n2;i+=2) { sum+=i; num++; if(num==m&&(i!=n

2)) { printf("%d “,sum/num); num-=m; sum=0; } } if(sum0&&num0) continue; printf(”%d\n",sum/num); }}③簡要題意有一樓梯共M級,剛開始時你在第一級,若每次只能跨上一級或二級,要走上第M級,共有多少種走法?解題思路:因為最後一步可以跨一個可以跨兩個,跨上第n臺階的等於跨上第n-1臺階的數量+跨上第n-2臺階的數量。解題細節:斐波那契數列 f(n)=f(n-1)+f(n-2);由於後續資料較大,所以要用__int64型別,否則會因溢位而導致WA,還有使用printf輸出時應是"%I64d"。原碼:#include<stdio.h> int main() { int N=0,M=0,i=0,sum=0,a[41]={0}; scanf("%d",&N); a[1]=1; a[2]=1; for(i=3;i<=40;i++) //將前40個數儲存起來 a[i]=a[i-1]+a[i-2]; while(N–) { scanf("%d",&M); printf("%d\n",a[M]); } return 1; }總結:方程求解,第一是想到遞迴。可是遞迴的時間複雜度太大,題目中說的是你現在已經是站在第一級上了.我們在寫程式的時候後退一步,假設人在第0級。那麼這時候我們就很容理解為什麼dp[1]1dp[2] == 2了。單數這時候需要注意的是題目中說的上到底3級,其實也就是上到我們程式中所說的第2級。這是需要注意的.這也是為什麼輸出最後的結果是dp[n-1]了④簡要題意:輸入一個十進位制數N,將它轉換成R進位制數輸出。解題思路:十進位制的數轉成二進位制,最常用的方法,就是除R取餘法,將十進位制的數n除以R取其餘數,這裡得到的餘數是R進位制數的最後一位。比如:7轉化為2進位制,先用7%2得到的是1,這裡的1是轉化後的二進位制數的最後一位,再接著,令n=n/R,即為除得的整數結果(注意,這裡的n需要大於0,因為後續會產生重複的操作),同上例n=7/2=3;再同以上的方法將,3%2=1作為2進位制數的倒數第二位。以此類推,2進位制數的倒數第三位等於3/2%2=1;這時n=0了,迴圈到此終止,二進位制的數為111(注意是從第1位讀到最後一位),即是說,程式碼實現時需要逆序輸出(這裡很容易想到用陣列來存取每一個餘數)。逆序輸出我們很熟悉,若R進位制的數共有X位,令n=X-1;n往下移動一位一直到n=0,對陣列進行輸出。for迴圈可以方便的實現。到此一個十進位制的數轉化成10進位制以內任意R進位制功能就實現了。在接下來,考慮11到16進位制的轉化,很明顯,因為有了字母的引入。不能直接對於陣列進行直接的輸出。這裡,可以使用switch case 或者是if語句。倒順序的每次判斷均輸出一個結果,輸出結束別忘了加上\n換行。解題細節:PS別忘了對於0以及負數的單獨判斷。負數先轉換為正數,按以上的方法輸出後,再加上一個符號位(這裡的符號位要在最開始輸出);0則是直接等於0;原碼:#include<stdio.h>#include<string.h>int main(){ int n,r,i; while(scanf("%d %d",&n,&r)!=EOF) { if(n<0) { printf("-");n=-n; } if(n
0){printf(“0\n”);continue;} int c=0,a[100]; while(n) { a[c]=(n%r); c++; n/=r; } for(i=c-1;i>=0;i–) { if(a[i]>=10) { printf("%c",‘A’+a[i]-10); } else printf("%d",a[i]); } printf("\n"); }}總結:要熟悉進位制之間的轉換。⑤簡要題意:有如下方程:Ai = (Ai-1 + Ai+1)/2 - Ci (i = 1, 2, 3, … n).若給出A0, An+1, 和 C1, C2, …Cn.請程式設計計算A1 = ? 解題思路:因為:Ai=(Ai-1+Ai+1)/2 - Ci, A1=(A0 +A2 )/2 - C1; A2=(A1 + A3)/2 - C2 , … => A1+A2 = (A0+A2+A1+A3)/2 - (C1+C2) => A1+A2 = A0+A3 - 2(C1+C2) 同理可得: A1+A1 = A0+A2 - 2(C1) A1+A2 = A0+A3 - 2(C1+C2) A1+A3 = A0+A4 - 2(C1+C2+C3) A1+A4 = A0+A5 - 2(C1+C2+C3+C4) … A1+An = A0+An+1 - 2(C1+C2+…+Cn) ----------------------------------------------------- 左右求和 (n+1)A1+(A2+A3+…+An) = nA0 +(A2+A3+…+An) + An+1 - 2(nC1+(n-1)C2+…+2Cn-1+Cn) => (n+1)A1 = nA0 + An+1 - 2(nC1+(n-1)C2+…+2Cn-1+Cn) => A1 = [nA0 + An+1 - 2(nC1+(n-1)C2+…+2Cn-1+Cn)]/(n+1) 解題細節:原碼:#include #include #include #include using namespace std;const int maxn=3000+10;double c[maxn];int main(){ int n; double A0,An; while(scanf("%d",&n)!=EOF) { scanf("%lf%lf",&A0,&An); double sum=0.0; for(int i=1;i<=n;i++) { scanf("%lf",&c[i]); sum+=(n-i+1)c[i]; } double A1 = (n
A0+An-2*sum)/(n+1); printf("%.2lf\n",A1); } return 0;}⑥第一天吃掉了所有桃子總數一半多一個,第二天又將剩下的桃子吃掉一半多一個,以後每天吃掉前一天剩下的一半多一個,到第n天準備吃的時候只剩下一個桃子。第一天開始吃的時候一共有多少個桃子?解題思路、解題細節最後剩下一個 ,每天都吃前一天剩下的一半多一個,倒著推唄。原始碼#include <bits/stdc++.h>using namespace std;int main(){ int n; while (cin>>n) { if (n==0) break; int i,c=1; for (i=n;i>1;i–) c=(c+1)*2; cout<<c<<endl; } return 0;}⑦輸入資料的第一行是三個整數K , M , N,分別表示可能的組合數目,女生的人數,男生的人數。0<K<=1000 ,1<=N 和M<=500.接下來的K行,每行有兩個數,分別表示女生Ai願意和男生Bj做partner。最後一個0結束輸入。 Output對於每組資料,輸出一個整數,表示可以坐上過山車的最多組合數。 首先需要講下匈牙利演算法的基本思想,因為書上的確實很讓人看著煩躁,所以從網上找了一個簡明易了的。匈牙利演算法是由匈牙利數學家Edmonds於1965年提出,因而得名。匈牙利演算法是基於Hall定理中充分性證明的思想,它是部圖匹配最常見的演算法,該演算法的核心就是尋找增廣路徑,它是一種用增廣路徑求二分圖最大匹配的演算法。-------等等,看得頭大?那麼請看下面的版本:通過數代人的努力,你終於趕上了剩男剩女的大潮,假設你是一位光榮的新世紀媒人,在你的手上有N個剩男,M個剩女,每個人都可能對多名異性有好感(-_-||暫時不考慮特殊的性取向),如果一對男女互有好感,那麼你就可以把這一對撮合在一起,現在讓我們無視掉所有的單相思(好憂傷的感覺),你擁有的大概就是下面這樣一張關係圖,每一條連線都表示互有好感。本著救人一命,勝造七級浮屠的原則,你想要儘可能地撮合更多的情侶,匈牙利演算法的工作模式會教你這樣做:===============================================================一: 先試著給1號男生找妹子,發現第一個和他相連的1號女生還名花無主,got it,連上一條藍線二:接著給2號男生找妹子,發現第一個和他相連的2號女生名花無主,got it三:接下來是3號男生,很遺憾1號女生已經有主了,怎麼辦呢?我們試著給之前1號女生匹配的男生(也就是1號男生)另外分配一個妹子。(黃色表示這條邊被臨時拆掉)與1號男生相連的第二個女生是2號女生,但是2號女生也有主了,怎麼辦呢?我們再試著給2號女生的原配()重新找個妹子(注意這個步驟和上面是一樣的,這是一個遞迴的過程) 此時發現2號男生還能找到3號女生,那麼之前的問題迎刃而解了,回溯回去2號男生可以找3號妹子~~~ 1號男生可以找2號妹子了~~~ 3號男生可以找1號妹子所以第三步最後的結果就是:四: 接下來是4號男生,很遺憾,按照第三步的節奏我們沒法給4號男生騰出來一個妹子,我們實在是無能為力了……香吉士同學走好。這就是匈牙利演算法的流程,其中找妹子是個遞迴的過程,最最關鍵的字就是“騰”字。這便是匈牙利演算法的思想。程式碼:#include using namespace std;const int N = 505;bool map[N][N], flag[N];int pre[N];int n, m, num;//匈牙利演算法(二分圖演算法) int find(int cur) //cur為當前女生 { int i; for (i = 1; i <= m; i++) //被匹配的男生 { if (map[cur][i] && !flag[i]) //該男生未被匹配 並且被女生選中 { flag[i] = true; //這次匹配中,標記該男生已匹配 if (pre[i] == -1 || find(pre[i]))//該男生沒有被女生匹配 or 匹配了該男生的女生繼續找其他男生 -> ok { pre[i] = cur; //男生[i]跟女生cur配對了 return 1; } } } return 0;}int main(){ int i, girl, boy, sum; while (cin>>num) { if(!num)return 0; cin>>n>>m; //n是女生數量,m是男生數量 memset(map, false, sizeof(map)); memset(pre, -1, sizeof(pre)); for (i = 0; i < num; i++) { cin>>girl>>boy; map[girl][boy] = true; //可以匹配 } sum = 0; for (i = 1; i <= n; i++) //女生去匹配男生 { memset(flag, 0, sizeof(flag)); //每次重新標記0 sum += find(i); } cout<<sum<<endl; } return 0;}⑧ 已知兩點座標要求其到原點連線的夾角,那麼我們可以用向量去做,用向量的積除以向量模的乘積,在求其反函式,然後轉換為角度就可以知道其角度為多少了;解題細節:向量的模的乘積 sqrt(x2+y2);向量的乘積 x1x2+y1y2;原始碼:#include#includeusing namespace std;#define PI 3.1415967int main(){ double x1, x2, y1, y2; double mo, ji; int t; scanf("%d", &t); while (t–) { scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2); mo = sqrt(x1x1 + y1y1)sqrt(x2x2 + y2y2); ji = x1x2 + y1y2; printf("%.2lf\n", acos(ji / mo) / PI180.0); } return 0;}⑨ 如果兩個數中任何一個數都是另一個數的真約數之和,則這兩個數就是親和數。解題思路:用一個一維陣列記錄下來所輸入的數,並且將其真約數的和記錄下來,判斷與另一個數是否相等,反之一次,如果均相等給出YES,否則NO。解題細節:原始碼:#includeusing namespace std;int main(){ int s; cin>>s; while(s–) { int i,n,a,b; cin>>a>>b; int sum=0,m=0; for(i=1; i<a; i++) { if(a%i0) { sum+=i; } } for(n=1; n<b; n++) { if(sum%n0) { m+=n; } } if(ma&&sumb) cout<<“YES”<<endl; else cout<<“NO”<<endl; }return 0;}⑩簡要題意:把一個偶數拆成兩個不同素數的和 解題思路:兩個不同的素數加和等於給出的偶數.就算是一種方式 問一共有多少種方式.首先我們看到題馬上就會有一個for迴圈的思路去判斷小於n的素數.如果是素數 再去找另外一個素數.令a=n-當前素數,然後判斷a是否是素數就行了.推論到這裡我們有一個for迴圈能夠解決當前問題:for(int i=3;i<n;i++){ if(isprime(i)&&isprime(n-i))output++;}但是這裡有個問題:方式會重複 那麼我們就需要/2的操作之類的繁瑣判斷過程 所以我們要優化.1.我們知道偶數不可能是素數2.我們知道如果兩個素數不同.一定是一個素數小於t/2一個素數大於t/2沒有任何其他例項反駁掉這個結論之後我們就能優化為: for(int i=3;i<t/2;i+=2) { if(isprime(i)&&isprime(t-i)) output++; }#include<stdio.h>#include<math.h>#include<string.h>using namespace std;int isprime(int n){ int i; for(int i=2;i<=sqrt(n);i++) { if(n%i0) return 0; } return 1;}int main(){ int t; while(~scanf("%d",&t)) { if(t0)break; int output=0; for(int i=3;i<t/2;i+=2) { if(isprime(i)&&isprime(t-i)) output++; } printf("%d\n",output); }}11有一隻經過訓練的蜜蜂只能爬向右側相鄰的蜂房,不能反向爬行。請程式設計計算蜜蜂從蜂房a爬到蜂房b的可能路線數。原始碼#include <stdio.h>#include <stdlib.h>int main(){ int n; scanf("%d",&n); int start,direction; double way[51]={0}; way[2]=1; way[3]=2; for(int i=4;i<=50;i++) way[i]=way[i-1]+way[i-2]; for(int i=1;i<=n;i++){ scanf("%d%d",&start,&direction); printf("%.0f\n",way[direction-start+1]); } return 0;}12 龜兔賽跑解題思路:這題的思路應該不是很明顯的,一開始想到的是以每一個點一個狀態f[i]表示到達這一點的最短時間,但是就會發現加油站和普通的點的決策不相同,這處理起來有點麻煩,此外狀態之間怎麼轉移也是個問題,這個方法具體為什麼不行還是沒有想清楚,先說一下題結的思路吧,題解中是把是否加油當作我們唯一的決策(一眼看上去好像並不正確,應為決策還包括是否使用電動車,但是仔細想一想電動車的使用應該是隻要可以使用就使用,其實不應該構成決策),這樣以來我們的狀態就定義在加油站點就可以,這樣不難退出狀態轉移方程#include #include #include using namespace std;int main(int argc, const char * argv[]) int l; while(scanf("%d",&l)!=EOF) { int n,c,t,vr,vt1,vt2,s[150]; double f[150]; memset(s,0,sizeof(s)); memset(f,0,sizeof(f)); scanf("%d%d%d",&n,&c,&t); scanf("%d%d%d",&vr,&vt1,&vt2); s[0]=0; s[n+1]=l; for(int i=1;i<=n;i++) scanf("%d",&s[i]); for(int i=1;i<=n+1;i++) { double minimum=999999999; for(int j=0;j<i;j++) { double time=0; int len=s[i]-s[j]; if(len<=c) time=(double)((double)len/(double)vt1); else { time=(double)((double)c/double(vt1))+(double)((double)(len-c)/(double)vt2); } if(j!=0) time+=t; if(f[j]+time<minimum) minimum=f[j]+time; } //cout<<minimum<<endl; f[i]=minimum; } //cout<<f[n+1]<<endl; double r=(double)l/(double) vr; if(f[n+1]-r>1e-8) cout<<“Good job,rabbit!”<<endl; else cout<<“What a pity rabbit!”<<endl; } return 0;}13 簡要題意 每組資料的菜價就是數量乘上單價啊解題細節 在支付的時候採用四捨五入的方法把分頭去掉原始碼#include #include using namespace std;int main(){ double sum=0; char a[50]; double x,y; while(cin>>a>>x>>y) { sum+=xy; } printf("%.1f\n",sum); return 0;}14簡要題意一個整數,只知道前幾位,不知道末二位,被另一個整數除盡了,求該數的末二位解題細節原始碼#include <stdio.h>#include <stdlib.h>int main(){ int i,a,b,s[100],k; while(~scanf("%d%d",&a,&b)&&(a||b)) { k=0; a=a100; for(i=0;i<100;i++) { if((a+i)%b0) { s[k]=i; k++; } } for(i=0;i<k-1;i++) { if(s[i]<10)printf(“0”); printf("%d “,s[i]); } if(s[k-1]<10)printf(“0”); printf(”%d\n",s[k-1]); } return 0;}15 簡要題意:每行包含一個字元和一個整數n(0<n<41),不同的字元表示不同的花紋,整數n表示等腰三角形的高。顯然其底邊長為2n-1。如果遇到@字元,則表示所做出來的樣板三角形已經夠了。解題思路:這道題目很容易PE,主要要注意2個點,第一:每一行的最後一個字母后面沒有空格(我最後一次PE就是沒有注意到這一點的),這點很關鍵;第二,每個測試資料之間有一個空行,換句話說,就是最後一個測試資料後面沒有空行。原始碼:#include using namespace std;int main(){ int n, flag=0; char c; while(cin>>c&&c!=’@’){ cin>>n; if(flag!=0) cout<<endl; for(int i=0; i<n-1; i++){ for(int j=n-i-2; j>=0; j–) cout<<" "; cout << c; if(i0) cout<<endl; else{ for(int k=i2-2; k>=0; k–) cout<<" "; cout << c << endl; } } for(int i=0; i<2n-1; i++) cout<<c; cout<<endl; flag = 1; //cout<<“exo me”<<endl; } return 0;}總結:通過這三天的學習,學到了更多的寫程式碼的技巧,語法熟練的是必須的;熟練使用 STL 程式設計,面向物件思想不是一天兩天就可以有的,需要不斷的歷練和實戰才能培養出來的;面向物件在C++中的實現方式一定要熟(繼承,派生,虛擬函式等);模板的實參演繹要搞清楚。多寫程式碼,多調程式。培養錯誤的感知能力。學會挖掘,多思考。