HDU 4085 斯坦纳树

 题目大意:

给定无向图,让前k个点都能到达后k个点(保护地)中的一个,而且前k个点每个需要占据后k个中的一个,相互不冲突

找到实现这个条件达到的选择边的最小总权值

这里很容易看出,最后选到的边不保证整个图是联通的

我们只要计算出每一个连通的最小情况,最后跑一遍dfs就能计算出答案了

那么用dp[i][j]表示 i 点为根得到联通状态为 j 的情况需要选到的边的最小总权值

这个用斯坦纳树的思想就可以做到的

对于每一个状态,都用spfa跑一遍得到最优解

dp[i][j] = min(dp[i][j] , dp[k][j]+w[i][k])

而对于每一个点来说就有 dp[i][j] = min(dp[i][j] , dp[i][k]+dp[i][j-k])  (k&j = k)

最后选出所有符合的状态的联通块,dfs找到最优解

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <vector>
  4 #include <queue>
  5 #include <iostream>
  6 using namespace std;
  7 #define pii pair<int,int>
  8 const int MAX = 1<<11;
  9 const int INF = 0x3f3f3f3f;
 10 int T , n , m , k;
 11 vector<pii> vec[55];
 12 int dp[55][MAX] , id[55];
 13 bool inq[55];
 14 queue<int> que;
 15 
 16 void init()
 17 {
 18     for(int i=1 ; i<=n ; i++) vec[i].clear();
 19     memset(dp , 0x3f , sizeof(dp));
 20 }
 21 
 22 void add_edge(int u , int v , int w)
 23 {
 24     vec[u].push_back(make_pair(v , w));
 25     vec[v].push_back(make_pair(u , w));
 26 }
 27 
 28 void spfa(int cur)
 29 {
 30     while(!que.empty()){
 31         int u = que.front();
 32       // cout<<"inq: "<<u<<endl;
 33         que.pop(); inq[u]=false;
 34         int l = vec[u].size();
 35         for(int i=0 ; i<l ; i++){
 36             int to = vec[u][i].first;
 37             if(dp[to][cur] > dp[u][cur]+vec[u][i].second){
 38                 dp[to][cur] = dp[u][cur]+vec[u][i].second;
 39                 if(!inq[to]){
 40                     inq[to] = true;
 41                     que.push(to);
 42                 }
 43             }
 44         }
 45     }
 46 }
 47 
 48 void get_id()
 49 {
 50     memset(id , 0 , sizeof(id));
 51     for(int i=1 ; i<=k ; i++){
 52         id[i] = i , dp[i][1<<(id[i]-1)] = 0;
 53         que.push(i);
 54         spfa(1<<(id[i]-1));
 55     }
 56     for(int i=k+1 , j=n; i<=2*k ; i++ , j--){
 57         id[j] = i , dp[j][1<<(id[j]-1)] = 0;
 58         que.push(j);
 59         spfa(1<<(id[j]-1));
 60     }
 61     for(int i=k+1 ; i<=n-k ; i++) dp[i][0] = 0;
 62 }
 63 
 64 void read_edge()
 65 {
 66     for(int i=0 ; i<m ; i++) {
 67         int u , v , w;
 68         scanf("%d%d%d" , &u , &v , &w);
 69         add_edge(u , v , w);
 70     }
 71 }
 72 
 73 void solve()
 74 {
 75     int all = 1<<(2*k);
 76     for(int cur=0 ; cur<all ; cur++){
 77         for(int i=1 ; i<=n ; i++){
 78             for(int p=cur&(cur-1) ; p ; p=(p-1)&cur){
 79                 if(dp[i][cur] > dp[i][p]+dp[i][cur-p]){
 80                     dp[i][cur] = dp[i][p]+dp[i][cur-p];
 81                     if(!inq[i]){
 82                         que.push(i);
 83                         inq[i]=true;
 84                     }
 85                 }
 86             }
 87         }
 88         spfa(cur);
 89     }
 90 }
 91 
 92 //求出得到的子树的最小代价
 93 int tree[MAX] , ok[MAX] , tot , ret; //ok[]记录那些合法的状态,也就是左半部分的1等于右半部分的1
 94 
 95 bool is_ok(int x)
 96 {
 97     int cnt = 0;
 98     for(int i=0 ; i<k ; i++) if(x&(1<<i)) cnt++;
 99     for(int i=k ; i<2*k ; i++) if(x&(1<<i)) cnt--;
100     return cnt == 0;
101 }
102 
103 void get_tree()
104 {
105     int all = 1<<(2*k);
106     tot = 0;
107     for(int cur=0 ; cur<all ; cur++){
108         tree[cur] = INF;
109         for(int i=1 ; i<=n ; i++) tree[cur] = min(tree[cur] , dp[i][cur]);
110         if(cur>0 && is_ok(cur)){
111             ok[++tot] = cur;
112            // cout<<tot<<" "<<cur<<" "<<tree[cur]<<endl;
113         }
114     }
115 }
116 
117 void dfs(int p , int cur , int cost , int all)
118 {
119     if(cost>ret) return;
120     if(cur == all){
121         ret = min(ret , cost);
122         return;
123     }
124     if(p>tot) return;
125     if(!(cur&ok[p])) dfs(p+1 , cur|ok[p] , cost+tree[ok[p]] , all);
126     dfs(p+1 , cur , cost , all);
127 }
128 
129 int main()
130 {
131    // freopen("a.in" , "r" , stdin);
132     scanf("%d" , &T);
133     while(T--)
134     {
135         scanf("%d%d%d" , &n , &m , &k);
136         init();
137         read_edge();
138         get_id();
139         solve();
140         get_tree();
141         ret = INF;
142         dfs(1 , 0 , 0 , (1<<(2*k))-1);
143         if(ret<INF) printf("%d
" , ret);
144         else puts("No solution");
145     }
146     return 0;
147 }
原文地址:https://www.cnblogs.com/CSU3901130321/p/4811807.html