#include <iostream>#include <cstring>#include <cstdlib>#include <cstdio>//#define INPUT/** Problem: poj3740 - Easy Finding Begin Time:8:30 p.m. 20th/Mar/2012 End Time: 8:42 p.m. 21st/Mar/2012 Last Time: Maybe 4hours-; Input: Standard output: Knowledge point: DFS+回溯+剪枝 State: WA x 1 , TLE x 1 -> AC Thought: --------------------AC後的總結---------------------- 1.定義一個數組selectedCol,表示目前選出了幾列中有"1". 2.在choose之前,要檢查selectedRow[i]是否為true. 如果是true,這行就選過了,就continue 3.在每次choose某行的時候,都在choose函數裡進行check if choose_row[i] == 1 && selectedCol[i] == 1 return false; if choose_row[i] && !selectedCol[i] => selectedCol[i] = 1; 但是在choose之前記得儲存selectedCol的狀態,以便return false之前進行回溯。 4.如果choose失敗的話,就要檢查是否已經找到了全1,如果找到了 isFound = true; 在遞迴函式裡檢查isFound = true的話就return true; 5.如果沒找到的話,記得回溯。這就要求在遞迴函式裡每次儲存selectedCol的狀態,失敗了進行回溯。 6.如果leftRow == 0 ,那麼就check,如果check,return true;,否則return false; 剪枝策略: 每層遞迴都是從 i = 0 to rowNum 開始選 如果上層遞迴選了i,那就下層遞迴從i+1開始選。 因為上層遞迴從0開始選,選到i的話[0,i]的情況已經由上層遞迴選完了。 而且行數跟順序無關 比如說 第一層遞迴選了1 那麼第二,第三……一直到最後一層遞迴,肯定是把第一次選1的所有情況都遍曆了, 第一層遞迴才能去選2 而且 1 2 3 4 5 6 和 6 5 4 3 2 1 這麼選擇是沒有區別的,所以下一層遞迴從ind+1開始就可以了 加上這個剪枝,653ms就過了。 更變態的思路: 由於是0,1我們可以按位表示嘛!然後按照規則算,應該會非常快~ 遞迴函式定義: f(int totalRow,int totalCol,int leftRow,int ind); totalRow表示總共有多少行 totalCol表示列數 leftRow表示還有幾行沒選 int ind表示從現在第幾行開始選。 --------------------AC前的塗鴉---------------------- 其實這道題跟STICK是差不多的,當然,狀態壓縮是必須的。 每行壓縮成一個數字,然後是否有幾個數字相加起來正好等於 1111111.... 注意,最多可以有10^300,所以這個比較囧! 每一個狀態就是按行i是否被選擇來決定, 選擇了某一行之後,就定義tmp[m]對應的列已經被佔領。 那麼剩下的就轉化為用leftNum - 1行來拼出來其他的列了,如果都能拼出來,那麼 return true,否則return false; 定義函數f(rowNum,colNum,leftNum);表示總共有rowNum列,有colNum行,剩leftNum沒有選 每次選擇的時候定義一個check,看看是否已經選出了每列全有1的行。 還有一個check2,看看當前選的行是否跟以前的行有衝突。**/using namespace std;int matrix[20][310]; ///開大一點總是沒錯的bool selectedCol[310];bool selectedRow[18];bool isFound;bool checked(const int colNum){ for(int i = 0 ; i < colNum ; i++) { if (!selectedCol[i]) return false; } return true;}bool choose(const int a[310],int colNum){ int tmp_state[310]; memset(tmp_state,0,sizeof(tmp_state)); memcpy(tmp_state,selectedCol,sizeof(tmp_state)); for(int i = 0 ; i < colNum ; i++) { if(!selectedCol[i] && a[i] ) { selectedCol[i] = a[i]; } else { if ( selectedCol[i] && a[i] ) { memcpy(selectedCol,tmp_state,sizeof(tmp_state)); return false; } } } return true;}bool Solve(int rowNum,int colNum,int leftNum,int ind){ int tmp_state[310]; memset(tmp_state,0,sizeof(tmp_state)); if( isFound ) return true; if (leftNum == 0) { if ( checked(colNum) ) { isFound = true; return true; } else { return false; } } for(int i = ind ; i < rowNum ; i++) { memcpy(tmp_state,selectedCol,sizeof(tmp_state)); if( isFound ) return true; if ( selectedRow[i] ) continue; if ( choose(matrix[i],colNum) ) { if( selectedRow[i] != true && !checked(colNum) ) { selectedRow[i] = true; Solve(rowNum,colNum,leftNum - 1,i+1); } else { if ( checked(colNum) ) { isFound = true; return true; } } // memcpy(matrix[i],tmp_state,sizeof(tmp_state)); memcpy(selectedCol,tmp_state,sizeof(selectedCol)); selectedRow[i] = false; } // selectedRow[i] = false; } return false;}int main(){ int n,m;#ifdef INPUT freopen("b:\\acm\\poj3740\\input.txt","r",stdin);#endif while(scanf("%d%d",&m,&n) != EOF ) { isFound = false; memset(matrix,0,sizeof(matrix)); memset(selectedCol,0,sizeof(selectedCol)); memset(selectedRow,0,sizeof(selectedRow)); for(int i = 0 ; i < m ; i++) { for(int j = 0 ; j < n ; j++) { scanf("%d",&matrix[i][j]); } } if ( Solve(m,n,m,0) ) { printf("Yes, I found it\n"); } else { printf("It is impossible\n"); } } return 0;}