標籤:

如何用 C++ 在 10 行內寫出八皇后?

去一家小的電商公司面試後台程序員職位,待遇15k。問題開始都比較簡單,直到最後出現極品問題,讓人慾哭無淚。讓我用c++在10行內寫出八皇后。我真的很想走人算了,但是又覺得可以搞定,最後居然沒搞出來。一個傻b的電商公司,有必要問八皇后嗎?真把自己當google嗎?對了,面試時還給了瓶維他命水,真沒心思喝。

補充:知乎影響力不是蓋的,該公司老闆@子飛 已來,大家散了吧。學習了很多,繼續努力找工作去了。


用 C 實現,只有一行一個語句,連空格 173 個字元:

main(a,l,r,m,i,j,k){return a?printf("%d",main(0,0,0,0,0,0,8)):j?i?main(0,l,r,m,ii-1,1,k)+main(0,(l|i-i)*2,(r|i-i)/2,m|i-i,0,0,k-1):0:k?main(0,l,r,m,~(l|r|m)255,1,k):1;}

編譯執行:

$ gcc -w -include stdio.h a.c ./a.out
92

這個解答從 @Comzyh 的 回答 壓縮,只是把迭代改成遞歸,以及用main()作為遞歸函數。

----

更新1:評論 @霧雨魔理沙 說到標準是不容許 main() 遞歸的,我再給一個標準一點的,兩個語句,連空格 154 個字元:

q(l,r,m,i,j,k){return j?i?q(l,r,m,ii-1,1,k)+q((l|i-i)*2,(r|i-i)/2,m|i-i,0,0,k-1):0:k?q(l,r,m,~(l|r|m)255,1,k):1;}main(){printf("%d",q(0,0,0,0,0,8));}

放不進一條 tweet。

----

更新2:去掉 k,147 個字元:

q(l,r,m,i,j){return j?i?q(l,r,m,ii-1,1)+q((l|i-i)*2,(r|i-i)/2,m|i-i,0,0):0:m==255?1:q(l,r,m,~(l|r|m)255,1);}main(){printf("%d",q(0,0,0,0,0));}

改為 n-queen,函數本身 117個字元,再加上列印 n=1...15(額外 61 個字元):

q(l,r,m,n,i,j){return j?i?q(l,r,m,n,ii-1,1)+q((l|i-i)*2,(r|i-i)/2,m|i-i,n,0,0):0:m==n?1:q(l,r,m,n,~(l|r|m)n,1);}
main(n){for(n=0;n++&<15;)printf("%d ",q(0,0,0,(1&<&

輸出

1 0 0 2 10 4 40 92 352 724 2680 14200 73712 365596 2279184

答案符合 A000170 - OEIS。

----

更新3:一行或 10 行都不好看,來個 20 行的。

q
(l,r,m,n,i,j)
{return
j?i?q(l,
r,m,n,
ii-1,1)+q((
l|i-i)*2
,(r|i-
i)/2,
m|i-i,
n,0,0):
0:m==
n?1:q
(l,r,
m,n,~
(l|r|m)
n,1);}main
(n){for(n=0;n
++&<15;)printf("%d " ,q(0,0,0,(1&<&

--

更新4: 把 m==n?a:b 改成 m-n?b:a,省一個字元,116個字元:

q(l,r,m,n,i,j){return j?i?q(l,r,m,n,ii-1,1)+q((l|i-i)*2,(r|i-i)/2,m|i-i,n,0,0):0:m-n?q(l,r,m,n,~(l|r|m)n,1):1;}
main(n){for(n=0;n++&<15;)printf("%d ",q(0,0,0,(1&<&

.


既然有人邀請我了,我就來了,解法參考 如何簡化求解八妃問題的代碼? - 知乎用戶的回答

#include &
#include &
#include &
#include &
#include &
int main() {
for (int queens[] = {0,1,2,3,4,5,6,7}; ::std::next_permutation(queens,queens+8); )
if ((::std::bitset&<15&>(::std::accumulate(queens,queens+8, ::std::make_pair(0, 0), [](::std::pair& a, int b){return ::std::make_pair((1&<&<(b+a.second))|a.first,a.second+1);}).first).count() == 8) (::std::bitset&<15&>(::std::accumulate(queens, queens+8, ::std::make_pair(0, 0), [](::std::pair& a, int b){return ::std::make_pair((1&<&<(7+b-a.second))|a.first, a.second+1);}).first).count() == 8)) ::std::cout &<&< queens[0] &<&< queens[1] &<&< queens[2] &<&< queens[3] &<&< queens[4] &<&< queens[5] &<&< queens[6] &<&< queens[7] &<&< ::std::endl; }

算上include,剛好十行,充分運用了C++標準庫

你們這些連include都沒的,也好意思貼上來么?

現在問題來了,15K 的工作在哪裡?

----------------------------------------------------------------------------

更新

J語言 49字元

(i.(([:*./"1[:(#=+/@:~:)"1(+,:-)"1)#])i.@:!A.i.)8


謝喵~

#include &
int sum,ans[8];
int solve(int n, long long mark, int *ans){
for (int i=n&>8?++sum0:0; n&>8i&<8; i!=7?std::cout &<&< ans[i++] &<&< " " : std::cout &<&< ans[i++] &<&< std::endl); for (int i=0; i&<8; !(mark&>&>i1)!(mark&>&>(n+i+7)1)!(mark&>&>(n-i+30)1)?solve(n+(ans[n-1]=i+1)-i, mark|1ll&<&

正好10行呢

輸出八皇后的所有方案和方案總數,這樣就完整了,直接輸出92是不是在抖機靈呢?

使用位運算來簡化行列攻擊判斷,mark的1-8位為列判斷,9-23與24-38位為對角線判斷.

//另:沒有main的,沒有include的,不能直接運行的應該不是嚴格符合題意吧.那這麼說我solve函數也只有5行呢... (逃


我覺的題主是來秀優越的!

像 @vczh,面試都沒拿到過維他命水好伐!


這是要輸出一個可行解還是所有解的個數?如果是個數的話,之前在quora上看到大神解答。

#include &

int main() {

std::cout &<&< 92 &<&< std::endl ;

return 0;

}


比行數有意思?

Python大法好

from itertools import *
cols = range(8)
for vec in permutations(cols):
if (8 == len(set(vec[i]+i for i in cols))
== len(set(vec[i]-i for i in cols))):
print vec

C++的話,隨隨便便就超過了,不過如果可以,呵呵...

#include&
using namespace std;int position[8];bool isSafe(int queen_number, int row_position) {for (int i = 0; i &< queen_number; i++) {int other_row_pos = position[i];if (other_row_pos == row_position ||other_row_pos == row_position - (queen_number - i) ||other_row_pos == row_position + (queen_number - i))return false;}return true;}void solve(int k){if (k == 8){for (int i = 0; i &< 8; i++)cout &<&< position[i] &<&< " ";cout &<&< endl;}else{for (int i = 0; i &< 8; i++){if (isSafe(k, i)){position[k] = i;solve(k + 1);}}}}int main(){ solve(0);return 0;}

使用了心形在線生成網站:ImageChef - Visual Poetry for Facebook or Email Greetings(娛樂貼)


n皇后(1&<=n&<=10)問題的通用行數,9行解決。

//////////////////////////////////////////////////////////////////////////////////////////////

/*

n皇后問題

1&<=n&<=10

Count=QUEENNUM*QUEENNUM-1;

Num=QUEENNUM;

Attack=0;

*/

#define QUEENNUM 2

int NQueen( int Count,int Num,__int64 Attack )

{

__int64 ONE=1;

if( (Num==0) || ( Count==-1) ) return (Num==0)?1:0;

__int64 CulAttack= (ONE&<&<(Count/QUEENNUM) ) | ((ONE&<&<((Count%QUEENNUM))+QUEENNUM)) | (ONE&<&<(QUEENNUM*2+(QUEENNUM+Count%QUEENNUM-Count/QUEENNUM))) | (ONE&<&<(QUEENNUM*4+(Count/QUEENNUM+Count%QUEENNUM)));

if( ((CulAttackAttack)!=0) ) return NQueen(Count-1,Num,Attack);

return NQueen(Count-1,Num,Attack)+NQueen(Count-1,Num-1,Attack|CulAttack);

}

//////////////////////////////////////////////////////////////////////////////////////////////

如果僅需解決8皇后問題,那就更簡單了

int EightQueen()

{

return 92;

}

//////////////////////////////////////////////////////////////////////////////////////////////

程序說明

-- 如果糾結於皇后之間不能互相攻擊,10行內是搞不定的,必須轉換問題

對n皇后問題

定義1: 建立(n+2)*(n+2)的棋盤,其中最外圍成為壁,共有4*(n+1)個節點,稱為壁節點

定義2: 每個皇后定義4種攻擊方式:-- | / (橫線,豎線,左斜線,右斜線)。

定理1: 每個皇后對壁節點攻擊8次,每一種攻擊各兩次。

推論:

情況1: 任意兩個皇后之間不能相互攻擊

情況2: 任意一個壁節點最多承受4次方式不同攻擊

當棋盤中存在n個皇后時,情況1 為 情況2 的充分必要條件。

結論:

僅需要判斷每個壁節點的攻擊方式不重複即可。

優化:

由於每個皇后攻擊的壁節點對稱,因此每種攻擊僅需一半的攻擊節點。

-- n

| n

2*n

/ 2*n

程序思想:

1. 將n*n的棋盤編號,0 -- n*n-1

2. 判斷,當皇后處於位置i時,其4種攻擊方式所攻擊的壁節點編號

3. 若某壁節點已經被攻擊,則皇后不得處於該位置

4. 若無壁節點被重複攻擊,則皇后可能處於該位置

5. 採用bit位標註,最大為64bit,因此該函數最多能夠解決到10皇后問題

//////////////////////////////////////////////////////////////////////////////////////////////


有維他命水還有什麼不滿足,google都是叫人6點起床大老遠跑google去,拖到中午才給你面試,還沒水的。顯然沒有把自己當google。

google是我人生中見到的唯一一個,會把一整天要面試的人,都讓你早上到公司然後慢慢等的,簡直不把來面試的當人看。

隨便寫了一個,不保證對,剛好8行。被人提醒了好像沒考慮斜線,反正原理差不多,不想寫了……

int 八皇后(uint64_t current = 0, uint64_t remains = 8, uint64_t rows = 0, uint64_t columns = 0) {
if (remains == 0) if (rows == 0x0101010101010101UL columns == 0x0101010101010101UL)
return 1;
if (current == 64) return 0;
uint64_t newRows = rows | 1 &<&< (current % 8 * 8); uint64_t newColumns = columns | 1 &<&< (current / 8 * 8); return 八皇后(current+1, remains-1, newRows, newColumns) + 八皇后(current+1, remains, rows, columns); }


昨晚有朋友把這個問題在微信上直接發給我,說知乎上在吐槽你公司的面試,我趕忙註冊來回答。其實任何面試都不能完全考察一個人的能力。如果面試沒表現好,不要為你自己擔心,因為你總會找到更好的地方展示自己。

我在美國UT-Austin念研究生的時候,暑假要找實習工作,去西雅圖微軟面試,被問到過八皇后問題。記得是2007年,面試官很nice。屋子裡面有一個小黑板,很快黑板就被我寫滿了,但是代碼還沒寫完。我猶豫是否要把代碼擦掉重新寫。面試官說,就這樣結束吧,你知道用遞歸就很好了。顯然,我沒拿到offer。我後來反思了很久,修正了自我,終於搞定了5-6個offer,最後去了Google。

八皇后是上編程第一堂課就接觸的,為啥搞不定呢?我反思後的自我修正是:代碼一定要夠簡練,否則很難把它在短時間內清晰地呈現給別人。我後來發現大多數演算法題目,核心代碼都不超過20行。

如果你能用簡練的代碼解決問題,你的面試官一般都是被你秒殺的。簡潔的代碼,意味著很多。有次我去Goldman Sachs,面試官給我了兩個演算法題目二選一。他出去沖咖啡,回來時我把兩個題目都寫完了。當然,我據掉了GS,因為我後來決定回國創業了。

對於面試問題「用c++在10行內寫出八皇后」,我期待是這樣的:

第一,這個問題有很多細節沒有說,所以要clarify問題。那麼就是向面試官發問,比如:8皇后的解怎麼輸出?我會告訴你,我們只要求輸出解的個數,其實,我們想要的是n皇后,對於任意一個n的值,我們要求輸出解的個數。n=1,輸出1; n=2, 輸出0; ...; n=8,輸出92; ...

其次,確定什麼樣的代碼算一行?為什麼要規定10行?我會說10行只是一個指導性的目標,真實的目的是希望代碼簡單明了。我們希望花更少的時間讀懂你的代碼。在工作中也是如此,你的隊員希望你的代碼簡練明了,一看就懂,最好沒有坑。如果非要算行數,那麼單獨的「{」是不用計算的,因為信息量很小。

當然了,能不能做出來不是最重要的。面試者如何approach這個問題很重要。

這個帖子裡面的很多回復都很牛x,拜讀了之後感覺我大中華人才濟濟。希望大家不介意我打個小廣告:本公司含我在內目前只有2名程序猿,需要大量的人才加入。公司的千萬級投資已在年前全部到位,歡迎大家來公司面試順帶拿維他命水喝。

最後,有人說不貼代碼都是耍流氓,所以也貼出我以前寫的代碼,還請大家輕點拍磚(得到n皇后的解及其個數,分別是iterative和recursive兩種方法):


唔,其實python可以2行就過的

真真切切兩行,每行一條語句哦

Talk is cheap,show you the code.

import base64
exec(base64.b64decode("ZnJvbSBpdGVydG9vbHMgaW1wb3J0ICoKY29scyA9IHJhbmdlKDgpCmZvciB2ZWMgaW4gcGVybXV0YXRpb25zKGNvbHMpOgogICAgaWYgKDggPT0gbGVuKHNldCh2ZWNbaV0raSBmb3IgaSBpbiBjb2xzKSkKICAgICAgICAgID09IGxlbihzZXQodmVjW2ldLWkgZm9yIGkgaW4gY29scykpKToKICAgICAgICBwcmludCB2ZWM="))

感謝 @藍色的代碼


隨手寫了一下,遠遠超過了10行。。。

int col[15], sum[15], _diff[15], *diff = _diff + 8, ans[15];

void dfs(int r) {
if (r == 9) {
for (int i = 1; i &<= 8; ++i) { cout &<&< ans[i] &<&< " "; } cout &<&< endl; } for (int c = 1; c &<= 8; ++c) { if (col[c] == 0 sum[r + c] == 0 diff[r - c] == 0) { col[c] = sum[r + c] = diff[r - c] = 1; ans[r] = c; dfs(r + 1); col[c] = sum[r + c] = diff[r - c] = 0; } } }


八皇后?十行連 N 皇后都寫出來了。

int totalNQueens(int n) {
int upperlim = (1 &<&< n) - 1, sum = 0; std::function& dfs = [](int row, int l, int r) {
if (row == upperlim) { ++sum; return; }
for (int cur = upperlim (~(row|l|r)), pos = 0; cur; dfs(row+pos, (l+pos)&<&<1, (r+pos)&>&>1)) {
pos = cur (-cur);
cur -= pos;
}
};
dfs(0, 0, 0);
return sum;
}

函數體內正好十行。


lisp的話10行足夠了

(defun queens (n optional (m n))
(if (zerop n)
(list nil)
(loop for solution in (queens (1- n) m)
nconc (loop for new-col from 1 to m
when (loop for row from 1 to n
for col in solution
always (/= new-col col (+ col row) (- col row)))
collect (cons new-col solution)))))


為啥你覺得自己可以10行內寫出八皇后。。。

#include &

int
main(int argc, const char *argvp[]) {
::eightQueen();

return 0;
}

8行寫的八皇后


把換行符替換成空格。


#include &
int main(){
printf("...o....
......o.
..o.....
.......o
.o......
....o...
o.......
.....o..
");
}


位運算簡介及實用技巧(三):進階篇(2)

Matrix67大牛寫的 人肉翻譯成C++就好了.


int n=0;

void NQ(int l, int d, int r)

{

for (int v = ~(l|d|r); int c = v-v; v-=c)

{

if (~(d|c) == 0)

n++;

else

NQ((l|c)&<&<1, d|c, (r|c)&>&>1);

}

}

int main()

{

NQ(0,-1&<&<8,0);

printf("%d", n);

}

這是我根據Matrix大神的程序,改寫成C語言的版本,N皇后(N&<=32)問題的通解.

如果只算有內容的行,正好10行.

解釋一下:

所有的變數(n除外)都表示棋盤上的一行.變數中的一位表示棋盤上的一格.

l是上面幾行的皇后向左下發出的射線佔據的位置.

d是上面幾行的皇后向下發出的射線佔據的位置.

r是上面幾行的皇后向右下發出的射線佔據的位置.

l|d|r是被上面的皇后威脅的位置.

v = ~(l|d|r)是這行可放皇后的位置

c = v-v從可以放皇后的位置中取出最右邊一位

v-=c去除遍歷過的位

~(d|c) == 0表示每一列都有皇后了,那就是找到一個解.

NQ((l|c)&<&<1, d|c, (r|c)&>&>1);表示把向左下,下,右下的射線延伸一格,加上本行的皇后發出的射線.

NQ(0,-1&<&<8,0);表示一開始就標記了只有右邊8列是可用的.要求解N皇后問題,只需把8換成N.


記得大一的時候彙編老師給我們吹牛,說他曾經有個學生能一行寫出八皇后,之後再也沒人能寫出,我不服,然後寫了個給他,不過實在是搞不到一行去。

下面程序能夠編譯運行,輸出8皇后解的數量。正好10行,沒有return 0;

#include &
int queen(int l, int r, int m, int k){
int ans = 0;
for (int i = (~(l | r | m)) 0xff; i; i -= i -i)
ans += queen((l | (i -i)) &<&< 1, (r | (i -i)) &>&> 1 , m | (i -i), k + 1);
return k == 8 ? 1 : ans;
}
int main(){
printf("%d
", queen(0, 0, 0, 0));
}


姿勢水平不夠啊,33行才寫完

#include&
#define abs(x) ((x)&<0?(x):(-(x))) void output(int n,int *ans) { for(int i=0;i&


推薦閱讀:

C/C++該採用怎樣的命名規則才能讓自己的代碼足夠清晰呢?
如何才能學到Qt的精髓?
C++20有望實現自動PIMPL嗎?
如何評價 Herb Sutter 的 C++ 提案:metaclasses?
如何評價c++的協程庫libgo?

TAG:C |