演算法競賽入門經典 紫書 第四章

2021-09-29 02:18:53 字數 4592 閱讀 4058

關於判斷素數的幾點

//該函式有嚴重缺點:

//不能用於n==1和n較大的情況

//在n接近int的最大值時:

//若i=46340時,i*i=2147395600//若i=46341時,i*i=2147488281超過了int的最大值,溢位變成了負數,就會繼續進行下去

intis_prime

(int n)

return1;

}//編寫函式的時候,應該盡量保證該函式能對任何合法引數得到正確的結果

//改進後

//優點:避免了每次重複計算sqrt(n)

//通過四捨五入避免了浮點誤差

intis_prime

(int n)

return1;

}

謹慎地使用全域性變數

呼叫棧描述的是函式之間的呼叫關係

由多個棧幀組成,每個棧幀對應著乙個未執行完的函式

棧幀中儲存了該函式的返回位址和區域性變數,因而不僅能在執行完畢後找到正確的返回位址,還能自然地保證了不同函式間的區域性變數互不相干——因為不同函式之間對應不同的棧幀

這有一段錯誤的程式

void

swap

(int

* a,

int* b)

這個程式錯誤的原因是:

指標 t 在使用之前必須賦值

t在賦值之前是不確定的。如果這個不確定的值所代表的記憶體單元恰好是能寫入的,那麼這段程式將正常工作;但如果不是,而是唯讀的,那麼程式就會可能崩潰

這有一段錯誤的程式:

int

sum(

int a)

return ans;

}

錯誤的原因:

sizeof(a)是無法得到陣列的大小的

因為,陣列作為引數傳遞給函式時,實際上只有陣列的首位址作為指標傳遞給了函式。

在函式定義中的int a等價於int *a。

在只有位址資訊的情況下,是無法知道陣列裡有多少個元素的

正確的做法:

加乙個引數,即陣列的元素個數

一般的,若p是指標,k是正整數,那麼p+k就是指標p後面的第k個元素,p-k是p前面的第k個元素,而如果p1和p2是型別相同的指標,則p2-p1是從p1到p2的元素個數

那麼我們對計算陣列和的函式可以用兩種寫法:

1.

int

sum(

int* begin,

int* end)

return ans;

}

int

sum(

int* begin,

int* end)

return ans;

}

上述兩種寫法都是左閉右開

第二種寫法更為普遍

把陣列作為指標傳遞給函式時,陣列內容是可以修改的。

如果要寫乙個「返回陣列「的函式,可以加乙個陣列引數,然後在函式中修改這個陣列的內容

在c語言的函式中,呼叫自己和呼叫其他函式並沒有任何本質區別,都是建立新棧幀,傳遞引數並修改當前**行。

在函式體執行完畢後,刪除棧幀,處理返回值並修改當前**行。

編譯後產生的可執行檔案裡都儲存著與作業系統相關的內容。

段。是指二進位制檔案內的區域,所有某種特定型別的資訊被儲存在裡面。

在可執行檔案裡,正文段用於儲存指令

資料段用於儲存已初始化的全域性變數

bss段用於儲存未賦值的全域性變數所需的空間

呼叫棧不儲存在可執行檔案中,而是在執行時建立

呼叫棧所在段稱為堆疊段

堆疊段也有自己的大小,不能被越界訪問,否則就會出現段錯誤

棧溢位,那麼就是每次遞迴呼叫都會往呼叫棧裡增加乙個棧幀,久而久之,就越界了。

在執行時,程式會動態建立乙個堆疊段,裡面存放著呼叫棧,因此儲存著函式的呼叫關係和區域性變數

區域性變數也是存放在堆疊段內的。

棧溢位不一定是遞迴呼叫太多,也可能是區域性變數太大

因此,我們一般把較大的陣列存放在main函式外

例題4-1 古老的密碼 uva1339

//qsort函式的宣告

void

qsort

(void

* base, size_t num, size_t size,

int(

*comparator)

(const

void*,

const

void*)

)//qsort函式實現的是快速排序演算法

//引數說明:

//待排序的陣列起始位址

//元素個數

//元素的大小

//乙個指向函式的指標,該函式必須具有以下行書

intcmp

(const

void*,

const

void*)

//這個函式中,const void *的意思是:

//指向常數的萬能的指標,它可以通過強制型別轉化變成任意型別的指標

//例如,如果排序的物件是個整型陣列,那麼:

intcmp

(const

void

* a,

const

void

* b)

//一般的,需要先把引數a和引數b轉化為真實的型別,然後讓cmp函式當ab時分別返回負數,0,和正數即可。

qsort在演算法競賽中不經常使用

經常使用sort函式

這裡是為了告訴「將乙個函式作為引數傳遞給另外乙個函式」是很有用的

例題4-2 劊子手遊戲 uva489

我們先來考慮,程式設計的方式

一般有兩種:

自頂向下和自底向上

演算法競賽中一般是自頂向下

即:先寫偽**,然後轉化為實際的**

先寫主程式,包括對函式的呼叫,在實現函式本身。

這個題沒有什麼難點,只是說注意一點就可以

**的注釋我都不想寫了

太簡單了

#include

#include

#define maxn 100

int left, chance;

char s[maxn]

, s2[maxn]

;int win, lose;

void

guess

(char ch)}if

(bad)

--chance;if(

!chance)

lose =1;

if(!left)

win =1;

}int

main()

if(win)

printf

("you win.\n");

else

if(lose)

printf

("you lose.\n");

else

printf

("you chickened out.\n");

}return0;

}

例題4-3 救濟金發放 uva133

這個需要注意的地方就是

迴圈的處理方式

這個算作是約瑟夫環的高階版

intgo(

int p,

int d,

int t)

while

(a[p]==0

);//跳過為0項

}return p;

}

例題4-4 資訊解碼 uva213

書上**:

#include

#include

intreadchar()

}//readint主要用於讀取01序列

intreadint

(int c)

//用於讀取c位二進位制字元,並轉換為十進位制數

return v;

}int code[8]

[1<<8]

;int

readcodes()

//用於讀取編碼,並形成編碼序列

}return1;

}int

main()

}putchar

('\n');

}return0;

}

習題4-1 uva1589

習題4-2 uva201

習題4-3 uva220

習題4-4 uva253

習題4-5 uva1590

習題4-6 uva508

習題4-7 uva509

習題4-8 uva12108

習題4-9 uva1591

習題4-10 uva815

習題4-11 uva1588

習題4-12 uva11809

《演算法競賽入門經典》第四章小結

題目 編寫乙個函式solve,給定浮點數a,b,c,d,e,f,求解方程組ax by c,dx ey f.任務1 使用assert巨集,讓解不唯一時異常退出。任務2 解不唯一時仍正常返回,但呼叫者有辦法知道解的數量 無解,唯一解,無窮多組解 思考 函式的引數都有哪些,各是什麼型別?任務1 inclu...

演算法競賽入門經典 第四章答案

4.4.1 小問題集錦 任務1 使用assert巨集,讓解不唯一時退出。include includevoid solve double a,double b,double c,double d,double e,double f,double x,double y int main 任務2 解不唯...

演算法競賽入門經典第四章習題

習題4 1 題目大意就是 乙個象棋殘局,紅方有n 2 n 7 個棋子,黑方只有乙個將,紅方除了帥,還可能有車,馬,炮,並且要考慮 蹩馬腿 輸入所有棋子的位置,保證局面合法並且紅方已經將軍,判斷紅方是否已經將黑方將死,解題思路 用乙個棋盤儲存紅方可以打到的地方,看黑方將是否有其餘的地方去,在豎直方向只...