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;
}
原文地址:https://www.cnblogs.com/yifeiWa/p/11336408.html