微策略2011校园招聘笔试题(找出数组中两个只出现一次的数字)
1、8*8的棋盤上面放著64個(gè)不同價(jià)值的禮物,每個(gè)小的棋盤上面放置一個(gè)禮物(禮物的價(jià)值大于0),一個(gè)人初始位置在棋盤的左上角,每次他只能向下或向右移動(dòng)一步,并拿走對(duì)應(yīng)棋盤上的禮物,結(jié)束位置在棋盤的右下角,請(qǐng)?jiān)O(shè)計(jì)一個(gè)算法使其能夠獲得最大價(jià)值的禮物。
//經(jīng)典的動(dòng)態(tài)規(guī)劃 //dp[i][j] 表示到棋盤位置(i,j)上可以得到的最大禮物值 //dp[i][j] = max( dp[i][j-1] , dp[i-1][j] ) + value[i][j] (0<i,j<n)int i, j, n = 8; dp[0][0] = value[0][0]; for( i = 1 ; i < n ; i++ ) {dp[i][0] = dp[i-1][0] + value[i][0]; } for( j = 1 ; j < n ; j++ ) {dp[0][j] = dp[0][j-1] + value[0][j]; }for( i = 1 ; i < n ; i++ ) {for( j = 1 ; j < n ; j++ ){dp[i][j] = max( dp[i][j-1] , dp[i-1][j] ) + value[i][j];} } cout<<dp[n-1][n-1]<<endl;擴(kuò)展:現(xiàn)在增加一個(gè)限定值limit,從棋盤的左上角移動(dòng)到右下角的時(shí)候的,每次他只能向下或向右移動(dòng)一步,并拿走對(duì)應(yīng)棋盤上的禮物,但是拿到的所有的禮物的價(jià)值之和不大于limit,請(qǐng)?jiān)O(shè)計(jì)一個(gè)算法請(qǐng)實(shí)現(xiàn)。
int row,col; int limit; int dp[row][col][limit];int getMaxLessLimit() {memset(dp,0,sizeof(dp));for(int r = row-1 ; r >= 0; r--){for(int c = col-1 ; c >= 0; c--){for(int l = 0 ; l <= limit; l++){if(l >= matrix[r][c]){int max = 0;if(r != row-1 && dp[r+1][c][l-matrix[r][c]]>max)max = dp[r+1][c][l-matrix[r][c]];if(c != col-1 && dp[r][c+1][l-matrix[r][c]]>max)max = dp[r][c+1][l-matrix[r][c]];if(max == 0 && !(r == row-1 && c == col-1))dp[r][c][l] = 0;elsedp[r][c][l] = max + matrix[r][c];}}}}return dp[0][0][limit]; }或者
int hash[row][col][limit]; int getMaxLessLimit() {memset(hash,0,sizeof(hash));hash[0][0][matrix[0][0]] = 1;for(int i = 0 ; i < row ; i++){for(int j = 0 ; j < col ; j++){for(int k = 0 ; k <= limit ; k++){if(k >= matrix[i][j]){if(i >= 1 && hash[i-1][j][k-matrix[i][j]])hash[i][j][k] = 1;if(j >= 1 && hash[i][j-1][k-matrix[i][j]])hash[i][j][k] = 1;}}}}int ans = 0;for(int k = limit; k >= 0; k--){if(hash[row-1][col-1][k] && k>ans)ans = k;}return ans; }2、有兩個(gè)字符串s1和s2,其長(zhǎng)度分別為l1和l2,將字符串s1插入到字符串s2中,可以插入到字符串s2的第一個(gè)字符的前面或者最后一個(gè)字符的后面,對(duì)于任意兩個(gè)字符串s1和s2,判斷s1插入到s2中后是否能夠構(gòu)成回文串。。
3、已知有m個(gè)頂點(diǎn),相鄰的兩個(gè)頂點(diǎn)之間有一條邊相連接,首尾頂點(diǎn)也有一條邊連接,這樣就構(gòu)成了一個(gè)圓環(huán)。
現(xiàn)在有一個(gè)二維數(shù)組M[][],M[i][j]=1時(shí),表明第i和j個(gè)節(jié)點(diǎn)之間有條邊存在,M[i][j]=0時(shí),表明第i和j個(gè)節(jié)點(diǎn)之間沒有邊存在,其中 M[i][i]=0,M[i][j]=M[j][i],輸入為一個(gè)二維數(shù)組M[][]和頂點(diǎn)的個(gè)數(shù)n,試著判斷該圖中是否存在兩個(gè)圓環(huán),且兩個(gè)圓環(huán)彼此之間沒有公共點(diǎn)。試著實(shí)現(xiàn)下面這個(gè)函數(shù):
bool IsTwoCircle(int **M,int n)
{
? ......
}
4、給定如下的n*n的數(shù)字矩陣,每行從左到右是嚴(yán)格遞增, 每列的數(shù)據(jù)也是嚴(yán)格遞增
1 3 7 15 16
2 5 8 18 19
4 6 9 22 23
10 13 17 24 28
20 21 25 26 33
現(xiàn)在要求設(shè)計(jì)一個(gè)算法, 給定一個(gè)數(shù)k 判斷出k是否在這個(gè)矩陣中。 描述算法并且給出時(shí)間復(fù)雜度(不考慮載入矩陣的消耗)
方法一
從右上角開始(從左下角開始也是一樣的),然后每步往左或往下走。
這樣每步都能扔掉一行或者一列,最壞的情況是被查找的元素位于另一個(gè)對(duì)角,需要2N步,因此這個(gè)算法是o(n)的,而且代碼簡(jiǎn)潔直接。
首先,我們注意到矩陣中的元素總是把整個(gè)矩陣分成四個(gè)更小的矩陣。例如,中間的元素9把整個(gè)矩陣分成如下圖所示的四塊。由于四個(gè)小矩陣也是行列有序的,問題自然而然地劃分為四個(gè)子問題。每次我們都能排除掉四個(gè)中的一個(gè)子問題。假如我們的查找目標(biāo)是21,21>9,于是我們可以立即排除掉9左上方的那塊,因?yàn)槟莻€(gè)象限的元素都小于或等于9。
以下是這種二維二分的代碼,矩陣的維度使用l、u、r、d刻畫的,其中l(wèi)和u表示左上角的列和行,r和d表示右下角的列和行。
bool quadPart(int mat[][N] , int key , int l , int u , int r , int d , int &row , int &col) {if(l > r || u > d)return false;if(key < mat[u][l] || key > mat[d][r])return false;int mid_col = (l+r)>>1;int mid_row = (u+d)>>1;if(mat[mid_row][mid_col] == key) //查找成功{row = mid_row;col = mid_col;return true;}else if(l == r && u == d)return false;if(mat[mid_row][mid_col] > key){ // 分別在右上角、左上角、左下角中查找return quadPart(mat , key , mid_col+1 , u , r , mid_row , row , col) ||quadPart(mat , key , l , mid_row+1 , mid_col , d , row , col) ||quadPart(mat , key , l , u , mid_col , mid_row , row , col) ;}else{ // 分別在右上角、左下角、右下角中查找return quadPart(mat , key , mid_col+1 , u , r , mid_row , row , col) ||quadPart(mat , key , l , mid_row+1 , mid_col , d , row , col) ||quadPart(mat , key , mid_col+1 , mid_row+1 , r , d , row , col) ;} }bool quadPart(int mat[][N] , int key , int &row , int &col) {return bool quadPart(mat , key , 0 , 0 , N-1 , N-1 , row , col); } 5、設(shè) 一個(gè)64位整型n,各個(gè)bit位是1的個(gè)數(shù)為a個(gè). 比如7, 2進(jìn)制就是 111, 所以a為3。
現(xiàn)在給出m個(gè)數(shù), 求各個(gè)a的值。要求代碼實(shí)現(xiàn)。
思路:如果可以直接用stl的bitset。算法可用移位和&1來做。還有一個(gè)更好的算法是直接位與(x-1)直到它為0。
while(n) {n = n & (n-1);++count; }6、有N+2個(gè)數(shù),N個(gè)數(shù)出現(xiàn)了偶數(shù)次,2個(gè)數(shù)出現(xiàn)了奇數(shù)次(這兩個(gè)數(shù)不相等),問用O(1)的空間復(fù)雜度,找出這兩個(gè)數(shù),不需要知道具體位置,只需要知道這兩個(gè)值。
?????? 求解:如果只有一個(gè)數(shù)出現(xiàn)過奇數(shù)次,這個(gè)就比較好求解了,直接將數(shù)組中的元素進(jìn)行異或,異或的結(jié)果就是只出現(xiàn)過奇數(shù)次的那個(gè)數(shù)。
????? 但是題目中有2個(gè)數(shù)出現(xiàn)了奇數(shù)次?,求解方法如下:
????? 假設(shè)這兩個(gè)數(shù)為a,b,將數(shù)組中所有元素異或結(jié)果x=a^b,判斷x中位為1的位數(shù)(注:因?yàn)閍!=b,所以x!=0,我們只需知道某一個(gè)位為1的位數(shù)k,例如0010 1100,我們可取k=2或者3,或者5),然后將x與數(shù)組中第k位為1的數(shù)進(jìn)行異或,異或結(jié)果就是a,b中一個(gè),然后用x異或,就可以求出另外一個(gè)。
????? 為什么呢?因?yàn)閤中第k位為1表示a或b中有一個(gè)數(shù)的第k位也為1,假設(shè)為a,我們將x與數(shù)組中第k位為1的數(shù)進(jìn)行異或時(shí),也就是將x與a外加上其他第k位為1的出現(xiàn)過偶數(shù)次的數(shù)進(jìn)行異或,化簡(jiǎn)即為x與a異或,結(jié)果是b。
???? 代碼如下:
7、找出數(shù)組中只出現(xiàn)一次的3個(gè)數(shù)。
思路類似于求解上題,關(guān)鍵是找出第一個(gè)來,然后借助上題結(jié)論求另外兩個(gè)。?
a[]數(shù)組,假設(shè)x y z為只出現(xiàn)一次的數(shù),其他出現(xiàn)偶數(shù)次
s^=a[i] 則x^y x^z y^z 的lowbit 這三個(gè)值有一個(gè)規(guī)律,其中肯定兩個(gè)是一樣的,另外一個(gè)是不一樣的。令flips=上述三個(gè)值的異或。因此,可以利用此條件獲得某個(gè)x(或者y,或者z),循環(huán)判斷的條件是 a[i]^s的lowbit==flips
解釋:a[i]^s即可劃分為兩組,一組是lowbit與flips不同,一組是lowbit與flips相同。這樣就能找到某個(gè)x,y,z,找出后,與數(shù)組最戶一個(gè)值交換,利用find2,找出剩余兩個(gè)。
lowbit為某個(gè)數(shù)從右往左掃描第一次出現(xiàn)1的位置。其實(shí) lowbit 函數(shù)里面寫成? mark = (x&(x-1))^x? ; return mark ; 也是可以的,功能是一樣的。
/** ? ** author :hackbuteer ** date?? :2011-10-19? ? **/ #include <iostream> ? using namespace std;int lowbit(int x) {return x & ~(x - 1); }void Find2(int seq[], int n, int& a, int& b) {int i, xors = 0;for(i = 0; i < n; i++)xors ^= seq[i];int diff = lowbit(xors);a = 0,b = 0;for(i = 0; i < n; i++){if( diff&seq[i] ) //與運(yùn)算,表示數(shù)組中與異或結(jié)果位為1的位數(shù)相同a ^= seq[i];elseb ^= seq[i];} }//三個(gè)數(shù)兩兩的異或后lowbit有兩個(gè)相同,一個(gè)不同,可以分為兩組 void Find3(int seq[], int n, int& a, int& b, int& c) {int i, xors = 0;for(i = 0; i < n; i++)xors ^= seq[i];int flips = 0;for(i = 0; i < n; i++) //因?yàn)槌霈F(xiàn)偶數(shù)次的seq[i]和xors的異或,異或結(jié)果不改變flips ^= lowbit(xors ^ seq[i]);//表示的是:flips=lowbit(a^b)^lowbit(a^c)^lowbit(b^c)//flips = lowbit(flips); 這個(gè)是多余的//三個(gè)數(shù)兩兩異或后lowbit有兩個(gè)相同,一個(gè)不同,可以分為兩組//所以flips的值為:lowbit(a^b) 或 lowbit(a^c) 或 lowbit(b^c)//得到三個(gè)數(shù)中的一個(gè)a = 0;for(i = 0; i < n; i++){if(lowbit(seq[i] ^ xors) == flips) //找出三個(gè)數(shù)兩兩異或后的lowbit與另外兩個(gè)lowbit不同的那個(gè)數(shù)a ^= seq[i];}//找出后,與數(shù)組中最后一個(gè)值交換,利用Find2,找出剩余的兩個(gè)for(i = 0; i < n; i++){if(a == seq[i]){int temp = seq[i];seq[i] = seq[n - 1];seq[n - 1] = temp;}}//利用Find2,找出剩余的兩個(gè)Find2(seq, n - 1, b, c); } //假設(shè)數(shù)組中只有2、3、5三個(gè)數(shù),2與3異或后為001,2與5異或后為111,3與5異或后為110, //則flips的值為lowbit(110)= 2 ,當(dāng)異或結(jié)果xors與2異或的時(shí)候,得到的就是3與5異或的結(jié)果110,其lowbit值等于flips,所以最先找出來的是三個(gè)數(shù)中的第一個(gè)數(shù):2int main(void) {int seq[]={ 2,3,3,2,4,6,4,10,9,8,8 };int a,b,c;Find3(seq, 11, a, b, c);cout<<a<<endl;cout<<b<<endl;cout<<c<<endl;return 0; }8、do while(0)在宏定義中的應(yīng)用。
#define?XYZ?\
do{...}?while(0)
這是一個(gè)奇怪的循環(huán),它根本就只會(huì)運(yùn)行一次,為什么不去掉外面的do{..}while結(jié)構(gòu)呢?原來這也是非常巧妙的技巧。在工程中可能經(jīng)常會(huì)引起麻煩,而上面的定義能夠保證這些麻煩不會(huì)出現(xiàn)。下面是解釋:
假設(shè)有這樣一個(gè)宏定義
#define?macro(condition)?\
if(condition)?dosomething()
現(xiàn)在在程序中這樣使用這個(gè)宏:
if(temp)
????macro(i);
else
????doanotherthing();
一切看起來很正常,但是仔細(xì)想想。這個(gè)宏會(huì)展開成:
if(temp)
????if(i)?dosomething();
else
????doanotherthing();
這時(shí)的else不是與第一個(gè)if語句匹配,而是錯(cuò)誤的與第二個(gè)if語句進(jìn)行了匹配,編譯通過了,但是運(yùn)行的結(jié)果一定是錯(cuò)誤的。
為了避免這個(gè)錯(cuò)誤,我們使用do{….}while(0) 把它包裹起來,成為一個(gè)獨(dú)立的語法單元,從而不會(huì)與上下文發(fā)生混淆。同時(shí)因?yàn)榻^大多數(shù)的編譯器都能夠識(shí)別do{…}while(0) 這種無用的循環(huán)并進(jìn)行優(yōu)化,所以使用這種方法也不會(huì)導(dǎo)致程序的性能降低。
此外,這是為了含多條語句的宏的通用性。因?yàn)槟J(rèn)規(guī)則是宏定義最后是不能加分號(hào)的,分號(hào)是在引用的時(shí)候加上的。
在MFC的afx.h文件里面, 你會(huì)發(fā)現(xiàn)很多宏定義都是用了do...while(0)或do...while(false)。
為了看起來更清晰,這里用一個(gè)簡(jiǎn)單點(diǎn)的宏來演示:
#define SAFE_DELETE(p) do{ delete p; p = NULL } while(0)
假設(shè)這里去掉do...while(0),
#define SAFE_DELETE(p)?? delete p; p = NULL;
那么以下代碼:
if(NULL != p) SAFE_DELETE(p)
else?? ...do sth...
就有兩個(gè)問題,
1) 因?yàn)閕f分支后有兩個(gè)語句,else分支沒有對(duì)應(yīng)的if,編譯失敗。
2) 假設(shè)沒有else, SAFE_DELETE中的第二個(gè)語句無論if測(cè)試是否通過,會(huì)永遠(yuǎn)執(zhí)行。
你可能發(fā)現(xiàn),為了避免這兩個(gè)問題,我不一定要用這個(gè)令人費(fèi)解的do...while,? 我直接用{}括起來就可以了
#define SAFE_DELETE(p)? ?{ delete p; p = NULL;}
的確,這樣的話上面的問題是不存在了,但是我想對(duì)于C++程序員來講,在每個(gè)語句后面加分號(hào)是一種約定俗成的習(xí)慣,這樣的話,以下代碼:
if(NULL != p)? SAFE_DELETE(p);
else?? ...do sth...
由于if后面的大括號(hào)后面加了一個(gè)分號(hào),導(dǎo)致其else分支就無法通過編譯了(原因同上),所以采用do...while(0)是做好的選擇了。
如果使用名家的方法
#define SAFE_FREE(p)?? do {free(p);p=NULL;}? while(0)
那么
if(NULL!=p)
??????? SAFE_FREE(p);
else
???????? do something
展開為
if(NULL!=p)
do
???? ?{ free(p);p=NULL;}
while(0);
else
??????? do something
好了 這樣就一切太平了。
總結(jié)
以上是生活随笔為你收集整理的微策略2011校园招聘笔试题(找出数组中两个只出现一次的数字)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅谈函数指针
- 下一篇: 一道网易游戏笔试题的不同解法