2019ICPC邀请赛南昌A Attack 斯坦纳树
先对8个点整体做斯坦纳树,再求出每个状态的最小代价。再看每个状态是否属于独立的,即包含的点都是成对的,我们去更新这些独立的状态即可。
1 #include <cstdio> 2 #include <cstring> 3 #include <string> 4 #include <map> 5 #include <queue> 6 #include <iostream> 7 using namespace std; 8 const int MAXN = 40,MAXM = 2100; 9 queue <int> que; 10 string s1,s2; 11 map <string,int> mp; 12 bool inq[MAXN]; 13 int head[MAXN],dis[MAXN][1 << 8],ans[1 << 8],spe[1 << 8],to[MAXM],nxt[MAXM],val[MAXM]; 14 int cnt,n,m,s,t,inf; 15 void add(int x,int y,int v) 16 { 17 nxt[++cnt] = head[x]; 18 to[cnt] = y; 19 val[cnt] = v; 20 head[x] = cnt; 21 } 22 void SPFA(int o) 23 { 24 while(!que.empty()) 25 { 26 int t = que.front(); 27 que.pop(); 28 inq[t] = false; 29 for(int i = head[t];i;i = nxt[i]) 30 { 31 if(dis[to[i]][o] > dis[t][o] + val[i]) 32 { 33 dis[to[i]][o] = dis[t][o] + val[i]; 34 if(!inq[to[i]]) 35 { 36 inq[to[i]] = true; 37 que.push(to[i]); 38 } 39 } 40 } 41 } 42 } 43 void steiner_tree() 44 { 45 for (int i = 0;i < (1 << 8);i++) 46 { 47 for (int j = 1;j <= n;j++) 48 { 49 for (int k = i;k;k = (k - 1) & i) 50 dis[j][i] = min(dis[j][i],dis[j][k] + dis[j][i - k]); 51 if (dis[j][i] != inf) 52 { 53 que.push(j); 54 inq[j] = true; 55 } 56 } 57 SPFA(i); 58 } 59 } 60 bool check(int o) 61 { 62 for (int i = 0;i <= 7;i += 2) 63 if (((o >> i) & 1) ^ ((o >> (i + 1)) & 1)) 64 return false; 65 return true; 66 } 67 int main() 68 { 69 while (scanf("%d%d",&n,&m) > 0) 70 { 71 cnt = 0; 72 memset(head,0,sizeof(head)); 73 memset(nxt,0,sizeof(nxt)); 74 memset(dis,0x1f,sizeof(dis)); 75 memset(ans,0x1f,sizeof(ans)); 76 inf = ans[0]; 77 int tv; 78 string s1,s2; 79 for (int i = 1;i <= n;i++) 80 { 81 cin >> s1; 82 mp[s1] = i; 83 } 84 for (int i = 1;i <= m;i++) 85 { 86 cin >> s1 >> s2; 87 scanf("%d",&tv); 88 add(mp[s1],mp[s2],tv); 89 add(mp[s2],mp[s1],tv); 90 } 91 for (int i = 1;i <= 8;i++) 92 { 93 cin >> s1; 94 dis[mp[s1]][1 << (i - 1)] = 0; 95 } 96 steiner_tree(); 97 for (int i = 0;i < (1 << 8);i++) 98 { 99 spe[i] = check(i); 100 for (int j = 1;j <= n;j++) 101 ans[i] = min(ans[i],dis[j][i]); 102 } 103 for (int i = 0;i < (1 << 8);i++) 104 if (spe[i]) 105 for (int j = i;j;j = (j - 1) & i) 106 if (spe[j] && spe[i - j]) 107 ans[i] = min(ans[i],ans[j] + ans[i - j]); 108 printf("%d\n",ans[(1 << 8) - 1]); 109 } 110 return 0; 111 }
心之所动 且就随缘去吧