如何用 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 個字元):
輸出 答案符合 A000170 - OEIS。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
更新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&<&
.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&
算上include,剛好十行,充分運用了C++標準庫
你們這些連include都沒的,也好意思貼上來么?現在問題來了,15K 的工作在哪裡?
----------------------------------------------------------------------------
更新J語言 49字元(i.(([:*./"1[:(#=+/@:~:)"1(+,:-)"1)#])i.@:!A.i.)8
謝喵~
正好10行呢#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&<&
使用位運算來簡化行列攻擊判斷,mark的1-8位為列判斷,9-23與24-38位為對角線判斷.
//另:沒有main的,沒有include的,不能直接運行的應該不是嚴格符合題意吧.那這麼說我solve函數也只有5行呢... (逃我覺的題主是來秀優越的!像 @vczh,面試都沒拿到過維他命水好伐!
這是要輸出一個可行解還是所有解的個數?如果是個數的話,之前在quora上看到大神解答。#include &
比行數有意思?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
#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;}
n皇后(1&<=n&<=10)問題的通用行數,9行解決。
//////////////////////////////////////////////////////////////////////////////////////////////
/*n皇后問題
1&<=n&<=10Count=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-12. 判斷,當皇后處於位置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&
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;
}
把換行符替換成空格。
#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 |