1. 程式人生 > >約數倍數選卡片

約數倍數選卡片

cst else 兩個 spl 理由 etl pen 有用 ||

問題描述

  閑暇時,福爾摩斯和華生玩一個遊戲:
  在N張卡片上寫有N個整數。兩人輪流拿走一張卡片。要求下一個人拿的數字一定是前一個人拿的數字的約數或倍數。例如,某次福爾摩斯拿走的卡片上寫著數字“6”,則接下來華生可以拿的數字包括:
  1,2,3, 6,12,18,24 ....
  當輪到某一方拿卡片時,沒有滿足要求的卡片可選,則該方為輸方。
  請你利用計算機的優勢計算一下,在已知所有卡片上的數字和可選哪些數字的條件下,怎樣選擇才能保證必勝!
  當選多個數字都可以必勝時,輸出其中最小的數字。如果無論如何都會輸,則輸出-1。 輸入格式   輸入數據為2行。第一行是若幹空格分開的整數(每個整數介於1~100間),表示當前剩余的所有卡片。

  第二行也是若幹空格分開的整數,表示可以選的數字。當然,第二行的數字必須完全包含在第一行的數字中。 輸出格式   程序則輸出必勝的招法!! 樣例輸入 2 3 6
3 6 樣例輸出 3 樣例輸入 1 2 2 3 3 4 5
3 4 5 樣例輸出 4

Algorithm

最開始這樣想,我們先選一個數,然後列舉出所有可能的情況。最後這是一棵樹......

所以就準備廣搜,搜了很久之後發現,廣搜好像不行,因為每個點廣搜只經過一次,但是我們的數字如果這樣不會輸可能還會拿來下一次進行下一次的方案中選取。

我沒能想到很好的方法來進行標記。

而深搜就不一樣了,每當這個數用過之後我們可以將它還原,以便於下一次接著用。

值得註意一點的地方是去重,和將數字排序一下。以及我們標記數字時,應該標記它還有幾個可以用,比如這裏有兩個2和兩個3,那麽應標記它出現了兩次,而不是出現過。

去重的加速可能不太可觀,但是排序便於我們選取。

我還看到了從大到小進行搜索的方法,理由是,大數的約數倍數更少,但是有個問題是,我們是要輸出最小的必贏的值......

這個程序遞歸的地方有點不好理解。先給自己記一筆......

這是我參考了諸多博客才實現的,有必要仔細研究一下。

技術分享圖片


AC

技術分享圖片
  1 /*
  2 * 萬能的搜索... 
  3 */ 
  4 #include<iostream>
  5 #include<string
> 6 #include<queue> 7 #include<vector> 8 #include<sstream> 9 #include<algorithm> 10 #include<cstring> 11 12 using namespace std; 13 14 const int MAX = 109; 15 vector<int> v[MAX]; 16 vector<int> c; 17 // 這裏的狀態標記不能這樣標記, 應標記出現多少次 18 int book[MAX]; 19 20 bool DFS(int n) 21 { // 尋找 n 的約數倍數, 如果沒有, 就會直接返回true(贏了) 22 // 1; 23 for(int i=v[n].size()-1;i>=0;i--){ 24 int now = v[n].at(i); 25 if(book[now]){ // 判斷當前數有沒有用完 26 book[now]--; 27 bool win = DFS(now); 28 book[now]++; 29 if(win) return false; 30 } 31 } 32 33 return true; 34 } 35 36 int main() 37 { 38 int x; 39 string s; 40 41 getline(cin, s); 42 stringstream ss(s); 43 // 統計每個數出現的次數 44 while(ss>>x) book[x]++; 45 getline(cin, s); 46 stringstream str(s); 47 // 我們可以選擇的數 48 while(str>>x) c.push_back(x); 49 // 排序, 方便從小到大搜, 搜到的第一個就是最小的值 50 sort(c.begin(), c.end()); 51 // 預處理可選數的約數和倍數 52 for(int i=1;i<MAX;i++) 53 if(book[i]) 54 for(int j=1;j<MAX;j++) 55 if(book[j] && (i%j == 0 || j%i == 0)) 56 v[i].push_back(j); 57 // 搜索 58 for(int i=0;i<c.size();i++){ 59 int now = c.at(i); 60 if(book[now]){ 61 book[now]--; 62 if(DFS(now)){ 63 cout<<now<<\n; 64 return 0; 65 } 66 // 用完之後一定要還, 不然下一次沒法搜 67 book[now]++; 68 } 69 } 70 cout<<-1<<\n; 71 72 return 0; 73 } 74 75 76 /* 77 void Init(int *a, int *b, int la, int lb) 78 { // 先把 a 約數倍數處理一下 79 // 想辦法去重... 80 sort(a, a+la); 81 // int *end = unique(a, a+la); 82 // la = end - a; 83 // a[i] 的約數和倍數是 a[j] 84 for(int i=0;i<la;i++) 85 for(int j=0;j<la;j++){ 86 if(i == j) 87 continue; 88 else if(i > 0 && a[i] == a[i-1]) 89 continue; 90 else if(a[i]%a[j] == 0 || a[j]%a[i] == 0) 91 v[a[i]].push_back(a[j]); 92 } 93 94 return; 95 } 96 97 int DFS(int start, int step) 98 { cout<<"DFS:"<<step<<endl; 99 // 發現可以贏就返回了..., 萬一輸了判斷不了啊... 100 if(book[start] == 0){ 101 // 如果一共進行了 奇數 次行動, 那麽獲勝 102 return (step&1 == 1)?1:0; 103 } 104 book[start]--; 105 int t = v[start].size(); 106 for(int i=0;i<t;i++){ 107 int num = v[start].at(i); 108 if(book[num] == 0) 109 continue; 110 // book[num]--; 111 DFS(num, step++); 112 book[num]++; 113 } 114 } 115 116 int BFS(int start) 117 { // 突然發現, 廣搜好像不好做, 因為標記這關不太好處理 118 // 可能會多次用到, 但是我們無法很好地還原前面一個點 119 queue<int> q; 120 q.push(start); 121 while(!q.empty()) 122 { 123 int t = q.front(); 124 book[t]--; 125 q.pop(); 126 int len = v[t].size(); 127 for(int i=0;i<len;i++){ 128 if(book[v[t].at(i)] == 0) 129 continue; // 這個數已經用完了 130 if(len > 1 && i != 0 && v[t].at(i) == v[t].at(i-1)) 131 continue; // 這個數已經進過一次隊了 132 int x = v[t].at(i); 133 q.push(x); 134 book[x]--; 135 break; 136 137 } 138 139 140 break; 141 } 142 143 144 return -1; 145 } 146 147 void fun(int *a, int *b, int la, int lb) 148 { 149 sort(b, b+lb); 150 memset(book, 0, sizeof(book)); 151 for(int i=0;i<la;i++) 152 book[a[i]]++; 153 154 int ret = 999; 155 Init(a, b, la, lb); 156 int c = 999; 157 for(int i=lb-1;i>=0;i--){ 158 memset(book, 0, sizeof(book)); 159 c = DFS(b[i], 1); 160 cout<<b[i]<<":->"<<c<<endl; 161 } 162 cout<<(c == 999)?-1:ret<<‘\n‘; 163 return; 164 } 165 166 int main() 167 { 168 string s1, s2; 169 int i = 0, j = 0; 170 171 getline(cin, s1); 172 stringstream ss1(s1); 173 while(ss1>>a[i++]); 174 175 getline(cin, s2); 176 stringstream ss2(s2); 177 while(ss2>>b[j++]); 178 179 fun(a, b, i-1, j-1); 180 181 return 0; 182 } 183 184 */
View Code 2019-02-24 16:29:08 技術分享圖片

約數倍數選卡片