洛谷P3959 寶藏

去年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 }
20分代碼

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 }
AC代碼

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 }
AC代碼

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 }
AC代碼

發現上面一列"而後"......語文水平有待提升啊......

相關文章
相關標籤/搜索