日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

震惊!递推与递归竟然可以这么编!%99的程序员都不知道!

發布時間:2023/12/3 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 震惊!递推与递归竟然可以这么编!%99的程序员都不知道! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

四、歸并排序(逆序對)

(一)、歸并排序
歸并排序是建立在歸并操作上的一種有效的排序算法,該算法是采用分治法(Divide
and Conquer)的一個非常典型的應用。將已有序的子序列合并,得到完全有序的序列;
即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合并成一個有序表,稱為二
路歸并。
例如有8 個數據需要排序:10 4 6 3 8 2 5 7
歸并排序主要分兩大步:分解、合并。
合并過程為:比較a[i]和a[j]的大小,若a[i]≤a[j],則將第一個有序表中的元
素a[i]復制到r[k]中,并令i 和k 分別加上1;否則將第二個有序表中的元素a[j]復制
到r[k]中,并令j 和k 分別加上1,如此循環下去,直到其中一個有序表取完,然后再將
另一個有序表中剩余的元素復制到r 中從下標k 到下標t 的單元。歸并排序的算法我們通
常用遞歸實現,先把待排序區間[s,t]以中點二分,接著把左邊子區間排序,再把右邊子區
間排序,最后把左區間和右區間用一次歸并操作合并成有序的區間[s,t]。

#include //歸并排序
#include
#include
#include
using namespace std;// 示例:7 5 8 15 8 45 2 5
int m[1001],r[1001];
void compare(int a,int b){
if(a==b) return;
int mid = (a+b) / 2;
compare(a,mid);
compare(mid+1,b);//這兩行是我一直沒有想通的,一百行for循環寫的很嗨皮。。。
int qian=a,hou=mid+1,tot=a;
while(qian<=mid&&hou<=b){
if(m[qian]<=m[hou]){
r[tot] = m[qian];
tot++;
qian++;
}
else{
r[tot] = m[hou];
tot++;
hou++;
}
}
while(qian<=mid){
r[tot]=m[qian];
tot++;
qian++;
}
while(hou<=b){
r[tot]=m[hou];
tot++;
hou++;
}//這兩個while不用再寫if了,也比我的思路好。
for(int i=a;i<=b;i++){
m[i]=r[i];
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&m[i]);
}
compare(1,n);
for(int i=1;i<=n;i++){
printf("%d ",m[i]);
}
}

(二)、逆序對

上述提到歸并排序是穩定的排序,相等的元素的順序不會改變,進而用其可以解決逆序
對的問題。首先我們了解一下什么是逆序對。
逆序對:設A 為一個有n 個數字的有序集(n>1),其中所有數字各不相同。如果存
在正整數i, j 使得1 ≤ i < j ≤ n 而且A[i] > A[j],則<A[i], A[j]> 這個
有序對稱為A 的一個逆序對,也稱作逆序數。
例如,數組(3,1,4,5,2)的逆序對有(3,1),(3,2),(4,2),(5,2),共4 個。
所謂逆序對的問題,即對給定的數組序列,求其逆序對的數量。
從逆序對定義上分析,逆序對就是數列中任意兩個數滿足大的在前,小的在后的組合。
如果將這些逆序對都調整成順序(小的在前,大的在后),那么整個數列就變的有序,即排
序。因而,容易想到冒泡排序的機制正好是利用消除逆序來實現排序的,也就是說,交換相
鄰兩個逆序數,最終實現整個序列有序,那么交換的次數即為逆序對的數量。
冒泡排序可以解決逆序對問題,但是由于冒泡排序本身效率不高,時間復雜度為
O(n^2),對于n 比較大的情況就沒用武之地了。我們可以這樣認為,冒泡排序求逆序對效
率之所以低,是因為其在統計逆序對數量的時候是一對一對統計的,而對于范圍為n 的序
列,逆序對數量最大可以是(n+1)*n/2,因此其效率太低。那怎樣可以一下子統計多個,
而不是一個一個累加呢?這個時候,歸并排序就可以幫我們來解決這個問題。
在合并操作中,我們假設左右兩個區間元素為:
左邊:{3 4 7 9} 右邊:{1 5 8 10}
那么合并操作的第一步就是比較3 和1,然后將1 取出來,放到輔助數組中,這個時候
我們發現,右邊的區間如果是當前比較的較小值,那么其會與左邊剩余的數字產生逆序關系,
也就是說1 和3、4、7、9 都產生了逆序關系,我們可以一下子統計出有4 對逆序對。接
下來3,4 取下來放到輔助數組后,5 與左邊剩下的7、9 產生了逆序關系,我們可以統計
出2 對。依此類推,8 與9 產生1 對,那么總共有4+2+1 對。這樣統計的效率就會大大提
高,便可較好的解決逆序對問題。
而在算法的實現中,我們只需略微修改原有歸并排序,當右邊序列的元素為較小值時,
就統計其產生的逆序對數量,即可完成逆序對的統計。

#include //基本完全copy上一題
#include
#include
#include
using namespace std;// 示例:7 5 8 15 8 45 2 5
int m[1001],r[1001],number=0;
void compare(int a,int b){
if(a==b) return;
int mid = (a+b) / 2;
compare(a,mid);
compare(mid+1,b);//這兩行是我一直沒有想通的,一百行for循環寫的很嗨皮。。。
int qian=a,hou=mid+1,tot=a;
while(qian<=mid&&hou<=b){
if(m[qian]<=m[hou]){
r[tot] = m[qian];
tot++;
qian++;
}
else{
r[tot] = m[hou];
tot++;
hou++;
number += mid-qian+1;//較上一題就加了這一行,一開始還直接無腦寫成了++。。。
}
}
while(qian<=mid){
r[tot]=m[qian];
tot++;
qian++;
}
while(hou<=b){
r[tot]=m[hou];
tot++;
hou++;
}//這兩個while不用再寫if了,也比我的思路好。
for(int i=a;i<=b;i++){
m[i]=r[i];
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&m[i]);
}
compare(1,n);
for(int i=1;i<=n;i++){
printf("%d “,m[i]);
}
printf(”\n%d",number);
}

走樓梯

時間限制: 1 Sec 內存限制: 128 MB
題目描述
樓梯有N 級臺階,上樓可以一步上一階,也可以一步上二階,編一個遞推程序,計算從第
一階走到第N 階共有多少種不同的方法。
輸入
輸入一個數N(1<=N<=10)
輸出
輸出共有多少種方法
樣例輸入
4
樣例輸出
5
解析:
這題是簡單的遞推題,首先,到第1 個臺階我們有1 種方法;到第2 個臺階,我們可
以在第0 個臺階上兩階,也可以在第1 個臺階上一階到達,即有2 種方法;對于之后第i
個臺階來說,同樣的方式,可以由第i-2 個臺階上兩階,也可以由第i-1 個臺階上一階到
達。令f[i]表示到第i 臺階的方法數,由上述分析可以得出f[i]=f[i-1]+f[i-2],初
始f[1]=1,f[2]=2,即可推得到達第n 個臺階的方法數了,復雜度O(n)。

//兩種方法差不多,有手就行

#include // 走樓梯(遞歸)
#include
#include
#include
using namespace std;
int jisuan(int n){
if(n1) return 1;
if (n2) return 2;
return jisuan(n-1)+jisuan(n-2);
}
int main(){
int n;
scanf("%d",&n);
printf("%d",jisuan(n));
}

#include // 走樓梯(遞推)
#include
#include
#include
using namespace std;
int main(){
int n,m[101];
scanf("%d",&n);
m[1] = 1;
m[2] = 2;
for(int i=3;i<=n;i++){
m[i] = m[i-1]+m[i-2];
}
printf("%d",m[n]);
}

過河卒【NOIP2002】

時間限制: 1 Sec 內存限制: 128 MB
題目描述
棋盤上的A 點有一個過河卒,需要走到目標B 點。卒行走的規則是:可以向下、或者向右。
同時在棋盤上的任一點有一個對方的馬(如C 點),對方的馬所在的點和所有跳躍一步可
達的點稱為對方馬的控制點(如圖中的C 點和P1,P2,…,P8)。卒不能通過對方馬的控制
點。棋盤用坐標表示,A 點(0,0),B 點(n,m)(n,m 為不超過20 的整數),同樣馬的
位置坐標是需要給出的,C≠A 且C≠B。現在輸入B 點和C 點的坐標,要你計算出過河卒
從A 點能夠到達B 點的路徑的條數。
圖2?1 過河卒
輸入
一行,4 個空格隔開的整數,表示(n,m)和C 點的坐標。
輸出
過河卒從A 點能夠到達B 點的路徑的條數。
樣例輸入
4 8 2 4
樣例輸出
0
解析:
這題的遞推思想和走樓梯的思路一樣,在坐標(i,j)位置時,它可以由(i-1,j)和
(i,j-1) 點過來, 令f[i][j] 表示到達(i,j) 位置時的方案數, 那么
f[i][j]=f[i-1][j]+f[i][j-1]。當然此題稍微增加了難點,也就是有障礙點,這些
點不能達到,所以他們的方案數特殊處理為0,我們可以在一開始預處理掉。

#include // 過河卒 bug不少。。。
#include
#include
#include
using namespace std;
int main(){
int a[23][23],n,m,I,J,i,j;
scanf("%d%d%d%d",&n,&m,&I,&J);
for(i=0;i<=n;i++){
for(j=0;j<=m;j++){
a[i][j]=1;
printf("%d “,a[i][j]);
}
printf(”\n");
}
printf("\n");
if(I-1>=0&&J-2>=0) a[I-1][J-2]=0;
if(I-2>=0&&J-1>=0) a[I-2][J-1]=0;
if(I-2>=0&&J+1>=0) a[I-2][J+1]=0;
if(I-1>=0&&J+2>=0) a[I-1][J+2]=0;
if(I+1>=0&&J+2>=0) a[I+1][J+2]=0;
if(I+2>=0&&J+1>=0) a[I+2][J+1]=0;
if(I+1>=0&&J-2>=0) a[I+1][J-2]=0;
if(I+2>=0&&J-1>=0) a[I+2][J-1]=0;
a[I][J]=0;
for(i=1;i<=n;i++) if(a[i][0]) a[i][0]=a[i-1][0];
for(i=1;i<=m;i++) if(a[0][i]) a[0][i]=a[0][i-1];
for(i=0;i<=n;i++){
for(j=0;j<=m;j++){
printf("%d “,a[i][j]);
}
printf(”\n");
}
printf("\n");
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
if(a[i][j]){
a[i][j]=a[i-1][j]+a[i][j-1];
}
printf("%d “,a[i][j]);
}
printf(”\n");
}
printf("%d",a[n][m]);
}

總結

以上是生活随笔為你收集整理的震惊!递推与递归竟然可以这么编!%99的程序员都不知道!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。