這回是兩道題一塊兒...ide
[USACO09NOV]燈spa
[中山市選2009]樹code
題意:給您一些燈,以及一些邊。每次改變一盞燈的時候,它相鄰的燈也會變。求把燈狀態所有轉換的最小操做次數。blog
解:it
解異或方程組的經典題。io
咱們對於燈列一個方程組,若是i對j有影響就令a[j][i] = 1event
而後解出上三角矩陣。class
解出來a[i][i] = 1的地方就是肯定的燈。cli
0就是無關緊要的燈。sed
而後咱們大暴力搜索無關緊要的燈。遇到肯定的燈就根據已經搜索的那些來肯定。
1 #include <cstdio> 2 #include <bitset> 3 #include <algorithm> 4 const int N = 40; 5 6 int n, p[N], ans = 0x3f3f3f3f; 7 std::bitset<N> a[N]; 8 9 inline void Gauss() { 10 for(int i = 1; i < n; i++) { 11 for(int j = i; j <= n; j++) { 12 if(a[j][i]) { 13 std::swap(a[i], a[j]); 14 break; 15 } 16 } 17 if(!a[i][i]) { 18 continue; 19 } 20 for(int j = i + 1; j <= n; j++) { 21 if(a[j][i]) { 22 a[j] ^= a[i]; 23 } 24 } 25 } 26 return; 27 } 28 29 inline void DFS(int k, int s) { 30 if(s >= ans) { 31 return; 32 } 33 if(k < 1) { 34 ans = std::min(ans, s); 35 return; 36 } 37 if(a[k][k]) { 38 int t = a[k][n + 1]; 39 for(int i = k + 1; i <= n; i++) { 40 if(a[k][i]) { 41 t ^= p[i]; 42 } 43 } 44 if(t) { /// need press 45 p[k] = 1; 46 DFS(k - 1, s + 1); 47 p[k] = 0; 48 } 49 else { 50 DFS(k - 1, s); 51 } 52 } 53 else { 54 DFS(k - 1, s); 55 p[k] = 1; 56 DFS(k - 1, s + 1); 57 p[k] = 0; 58 } 59 return; 60 } 61 62 int main() { 63 int m; 64 scanf("%d%d", &n, &m); 65 for(int i = 1, x, y; i <= m; i++) { 66 scanf("%d%d", &x, &y); 67 a[y].set(x); 68 a[x].set(y); 69 } 70 71 for(int i = 1; i <= n; i++) { 72 a[i].set(n + 1); 73 a[i].set(i); 74 } 75 76 Gauss(); 77 78 DFS(n, 0); 79 80 printf("%d", ans); 81 82 return 0; 83 }
一點思考:
咱們能不能把上三角矩陣消成最終的那種結果,而後搜索?這樣每次在DFS中肯定狀態就是O(1)的了。
咱們既然都消完了,能不能直接把 ans += a[i][i] & a[i][n + 1] ?
事實證實不行...
隨手造了點樣例發現過不了...
不由開始思考a[i][i] = 0的意義來。
好比這個最簡單的樣例:1和2之間有一條邊。
那麼消元以後的矩陣是這樣的:
1 1 1
0 0 0
這是啥啊.....搞不倒