去年NOIP第二毒瘤(並不)的題終於被我攻克了,接下來就只剩noip難度巔峯列隊了。算法
首先說一下三種作法:隨機化,狀壓DP和搜索。dom
前兩種作法我都A了,搜索實在是毒瘤,寫鬼啊。ide
有些帶DFS的記憶化搜索版狀壓DP,我看着感受有點問題......spa
就連隨機化裏面那個貪心我看起來也感受是錯的.....但仍是A了。3d
因此考試的時候不要虛,大膽寫隨機化,隨便混個幾十分,說不定就A了。code
20分算法:給定一棵樹,枚舉根便可。blog
1 #include <cstdio> 2 #include <algorithm> 3 4 const int N = 13, M = 1010, INF = 0x7f7f7f7f; 5 6 int G[N][N], n; 7 8 int DFS(int x, int k, int f) { 9 int ans = 0; 10 for(int i = 1; i <= n; i++) { 11 if(G[x][i] && i != f) { 12 ans += DFS(i, k + 1, x); 13 ans += G[x][i] * k; 14 } 15 } 16 return ans; 17 } 18 19 int main() { 20 int m; 21 scanf("%d%d", &n, &m); 22 for(int i = 1, x, y, z; i <= m; i++) { 23 scanf("%d%d%d", &x, &y, &z); 24 if(G[x][y]) { 25 z = std::min(z, G[x][y]); 26 } 27 G[x][y] = G[y][x] = z; 28 } 29 30 int ans = INF; 31 for(int i = 1; i <= n; i++) { 32 ans = std::min(ans, DFS(i, 1, 0)); 33 } 34 printf("%d", ans); 35 36 return 0; 37 }
40分算法:邊權相等,聽說能夠BFS/DFS/prim,反正我搞不出來....ip
70分算法:直接枚舉全排列,而後按照排列順序依次加點,貪心便可。博客
這裏有個問題:當前點的深度會對後面節點形成影響,因此貪心出來的不必定是這個順序的最優解。string
可是你有不少排列啊,說不定哪一次就搞出正解來了呢?
1 #include <cstdio> 2 #include <algorithm> 3 4 const int N = 13, M = 1010, INF = 0x7f7f7f7f; 5 6 int G[N][N], n, a[N], dis[N]; 7 8 int main() { 9 int m; 10 scanf("%d%d", &n, &m); 11 for(int i = 1, x, y, z; i <= m; i++) { 12 scanf("%d%d%d", &x, &y, &z); 13 if(G[x][y]) { 14 z = std::min(z, G[x][y]); 15 } 16 G[x][y] = G[y][x] = z; 17 } 18 19 for(int i = 1; i <= n; i++) { 20 a[i] = i; 21 } 22 int ans = INF; 23 do { 24 dis[a[1]] = 1; 25 int t_ans = 0; 26 for(int i = 2; i <= n; i++) { 27 bool f = 0; 28 int small = INF, pos; 29 for(int j = 1; j < i; j++) { 30 if(!G[a[i]][a[j]]) { 31 continue; 32 } 33 f = 1; 34 if(small > dis[a[j]] * G[a[i]][a[j]]) { 35 small = dis[a[j]] * G[a[i]][a[j]]; 36 pos = j; 37 } 38 } 39 if(!f) { 40 t_ans = INF; 41 goto flag; 42 } 43 t_ans += small; 44 dis[a[i]] = dis[a[pos]] + 1; 45 } 46 flag: 47 ans = std::min(ans, t_ans); 48 }while(std::next_permutation(a + 1, a + n + 1)); 49 50 printf("%d", ans); 51 return 0; 52 }
100分算法_隨機化:
把70分算法改進一下,不枚舉全排列,而是random_shuffle,說不定哪次就搞出正解來了呢?
而後就真A了......考場上還不是送分送到死啊,反向篩人。跑的還賊快...
1 #include <cstdio> 2 #include <algorithm> 3 4 const int N = 13, M = 1010, INF = 0x7f7f7f7f; 5 6 int G[N][N], n, a[N], dis[N]; 7 8 int main() { 9 int m; 10 scanf("%d%d", &n, &m); 11 for(int i = 1, x, y, z; i <= m; i++) { 12 scanf("%d%d%d", &x, &y, &z); 13 if(G[x][y]) { 14 z = std::min(z, G[x][y]); 15 } 16 G[x][y] = G[y][x] = z; 17 } 18 19 for(int i = 1; i <= n; i++) { 20 a[i] = i; 21 } 22 int ans = INF, T = 100000; 23 while(T--) { 24 std::random_shuffle(a + 1, a + n + 1); 25 dis[a[1]] = 1; 26 int t_ans = 0; 27 for(int i = 2; i <= n; i++) { 28 bool f = 0; 29 int small = INF, pos; 30 for(int j = 1; j < i; j++) { 31 if(!G[a[i]][a[j]]) { 32 continue; 33 } 34 f = 1; 35 if(small > dis[a[j]] * G[a[i]][a[j]]) { 36 small = dis[a[j]] * G[a[i]][a[j]]; 37 pos = j; 38 } 39 } 40 if(!f) { 41 t_ans = INF; 42 goto flag; 43 } 44 t_ans += small; 45 dis[a[i]] = dis[a[pos]] + 1; 46 } 47 flag: 48 ans = std::min(ans, t_ans); 49 } 50 51 printf("%d", ans); 52 return 0; 53 }
100分算法_狀壓DP:
好,這個纔是這篇博客的主要目的。
咱們發現這個深度很難搞啊...
而後又發現同一個深度的話,邊權的加權是必定的。
而後咱們考慮把邊權搞到狀態裏去,那就是f[i][j][k]表示根爲i,最大深度爲j,已選節點狀態是k的最小權值。
而後發現根那個維度其實能夠不要,因此就是f[i][j]表示最大深度爲i,目前狀態爲j的最小權值。
而後轉移就是枚舉j的子集k,計算出從k擴展成j的最小權值,乘上i便可。
而後發現上面那個計算k->j的最小權值,咱們對於每一個i都要作一次,因此能夠預處理出來。
而後搞一搞一些奇怪的細節,就A了...
具體見代碼。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 typedef long long LL; 6 const int N = 12, M = 1010, INF = 0x7f7f7f7f; 7 8 int G[N + 1][N + 1], n; 9 LL f[N + 1][1 << N], val[1 << N][1 << N]; 10 11 inline void out(int x) { 12 for(int i = 0; i <= 3; i++) { 13 printf("%d", (x >> i) & 1); 14 } 15 printf(" "); 16 } 17 18 int main() { 19 int m; 20 scanf("%d%d", &n, &m); 21 for(int i = 1, x, y, z; i <= m; i++) { 22 scanf("%d%d%d", &x, &y, &z); 23 if(G[x][y]) { 24 z = std::min(z, G[x][y]); 25 } 26 G[x][y] = G[y][x] = z; 27 } 28 29 int lm = 1 << n; 30 memset(f, 0x3f, sizeof(f)); 31 memset(val, 0x3f, sizeof(val)); 32 for(int i = 0; i < n; i++) { 33 f[0][1 << i] = 0; 34 } 35 for(int i = 1; i < lm; i++) { 36 for(int j = (i - 1) & i; j > 0; j = (j - 1) & i) { // j -> i 37 int ans = 0; 38 for(int k = 0; k < n; k++) { 39 if(!(i & (1 << k)) || (j & (1 << k))) { 40 continue; 41 } 42 int small = INF; 43 for(int l = 0; l < n; l++) { // l -> k 44 if(!(j & (1 << l)) || !G[l + 1][k + 1]) { 45 continue; 46 } 47 small = std::min(small, G[l + 1][k + 1]); 48 } 49 if(small == INF) { 50 ans = INF; 51 break; 52 } 53 ans += small; 54 } 55 val[j][i] = ans; 56 } 57 } 58 59 for(int i = 1; i <= n; i++) { // deep 60 for(int j = 1; j < lm; j++) { // state 61 for(int k = (j - 1) & j; k > 0; k = (k - 1) & j) { // subset 62 f[i][j] = std::min(f[i][j], f[i - 1][k] + val[k][j] * i); 63 /*printf("%d ", i); 64 out(k); 65 out(j); 66 printf("%lld + %lld \n", f[i - 1][k], val[k][j] * i);*/ 67 } 68 } 69 } 70 LL ans = 1ll * INF * INF; 71 for(int i = 0; i <= n; i++) { 72 ans = std::min(ans, f[i][lm - 1]); 73 } 74 printf("%lld", ans); 75 return 0; 76 }
發現上面一列"而後"......語文水平有待提升啊......