zoukankan      html  css  js  c++  java
  • UVa12093

    来自luogu的https://www.luogu.org/problemnew/solution/UVA12093

    的第二个题解,感谢!

    下面是我的思路,参考了博客,建议读博客,我的随便。

    // UVa 12093 
    /*
      通过读题就能发现这道题是一道树上的动态规划的问题,与UVa1218(完美的服务)有着相似之处,
      所以主要的思路就是将父结点与子结点的关系找出来,但是这一道题与UVa1218父、子结点关系
      都比较难找,所以需要先对问题进行分析并加以优化。
      首先对于无根树我们通常随机找一个点作为根就行了,这样能够通过dfs找出父子关系, 
      对于每一条边来说,覆盖它的可以是这条边两边的点,两边的点的额外的边对应的额外的点
      (说起来比较绕口),所以一条边可以有4个点来覆盖所以需要进行简化,如何简化?
      简化也是基于父子关系的,对于树上的动态规划,比较容易想到以xxx为根的子树,
      那么我们就考虑这个点与子结点的连边,为什么不考虑这个点与父结点的连边呢?
      其实我们是考虑的,只不过那个边应该在父结点的层面上考虑,这样说起来有一些
      抽象,但是有一定的规律,总之我们将问题简化掉了。
      简化后:考虑以u为根的子树覆盖所有边的最小花费,对于u来说,u的状态有很多种,
      选A、选B、不选......这三个我们比较容易想到,但是并没有跟u与子结点的边联系起来,
      所以我们重新定义状态:
      d(u,i)虑以u为根的子树覆盖所有边的最小花费,并且状态为i
      下面我说明一下i对应的值及其意义
      0 u不安装装置,与子结点连边被子结点覆盖
      1 u安装装置A
      2 u安装装置B
      3 u不安装装置,与子结点连边被父结点覆盖
      注意:子结点安装B等效于u安装A,所以需要判断是子结点安装B还是u安装A费用低。 
      上面的状态将所有父子连边的覆盖情况(肯定被覆盖,关键是被谁覆盖)就清楚了起来,下面说一下状态转移方程,
      为了简化,可能会采用的d(v,0,1,2,3)这种形式,v是u的子结点,0、1、2、3分别代表不同的情况
      转移分析: 
      0: 子结点需要安装A或B 1、2 
      1: 子结点可能安装A或B也可能不安装转移到0、1、2
      同时们看一下子结点安装B划算还是u安装A划算
      2: 转移到0、1、2、3
      3: 转移到0、1、2
      状态转移方程:
      d[u][0] = sum{ min(d[v][1,2]) }
      d[u][1] = sum{ min(d[v][0,1,2]) } + min(c1, d[v][2]-min(d[v][0,1,2])) }
      d[u][2] = sum{ min(d[v][0,1,2,3]) } + c2
      d[u][3] = sum{ min(d[v][0,1,2]) } 
      下面就是边界:
      d[叶节点][0,3] = 0 d[叶节点][1,2] = c(1,2)
      这类型的题思考方面也没有什么别的捷径,需要多练习多思考才能够出现灵感,
      还有就是将问题简化(例如c2能够覆盖长度为2的点所有的边,这不很复杂吗?所以需要我们简化成只考虑长度为1的边(父子连边)) 
    */ 
    #include <cstdio> 
    #include <cstring> 
    using namespace std;
    
    const int maxn=10000+5, INF=1000000003; 
    
    struct Edge{ int to, next; } edge[maxn<<1];
    int fa[maxn], id; 
    
    void add(int a, int b) {
      edge[++id] = (Edge){ b, fa[a] }; fa[a] = id; 
    }
    
    int Min(int a, int b) {
      return a < b ? a : b;  
    }
    
    int Min(int a, int b, int c) {
      return Min(Min(a,b), c); 
    }
    
    int Min(int a, int b, int c, int d) {
      return Min(Min(a,b,c), d);  
    }
    
    int n, c1, c2, d[maxn][4]; 
    
    void dfs(int u, int fa1) {
      d[u][0] = 0; d[u][1] = 0; d[u][2] = c2; d[u][3] = 0; 
      int x = INF;
      for (int i = fa[u]; i; i = edge[i].next) {
        int v = edge[i].to; 
        if (v == fa1) continue; 
        dfs(v, u); 
        d[u][0] += Min(d[v][1], d[v][2]); 
        int m = Min(d[v][0], d[v][1], d[v][2]);
        d[u][1] += m; 
        x = Min(x, d[v][2]-m);
        d[u][2] += Min(d[v][0], d[v][1], d[v][2], d[v][3]); 
        d[u][3] += Min(d[v][0], d[v][1], d[v][2]); 
      }
      d[u][1] += Min(c1, x);  
    }
    
    int main() { 
      while (scanf("%d%d%d", &n, &c1, &c2) == 3 && n) {
        memset(fa, 0, sizeof(fa)); id = 0; 
        for (int i = 1; i < n; ++i) {
          int a, b; 
          scanf("%d%d", &a, &b); 
          add(a, b); add(b, a); 
        }
        dfs(1, -1);
        int ans = Min(d[1][0], d[1][1], d[1][2]); 
        printf("%d
    ", ans);
      }
      return 0;
    }
  • 相关阅读:
    集合综合练习<三>
    集合综合练习<二>
    集合综合练习<一>
    java打分系统
    mysql存储过程
    mysql的视图、索引、触发器、存储过程
    mysql
    Java 集合底层原理剖析(List、Set、Map、Queue)
    HashMap底层实现
    Gradle
  • 原文地址:https://www.cnblogs.com/yifeiWa/p/11336408.html
Copyright © 2011-2022 走看看