zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    WPF PasswordBox不支持绑定解决方法
    获取当前日期是第几个星期
    WPF 事件触发命令
    EntityFramework使用动态Lambda表达式筛选数据
    C#发邮件
    Entity Framework 多对多查询的写法
    获取WPF窗口句柄
    C#调整图片亮度和对比度
    oracle 批量修改数据
    react前端自动化测试: jest + enzyme
  • 原文地址:https://www.cnblogs.com/CSU3901130321/p/4811807.html
Copyright © 2011-2022 走看看