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\n", ans); } return 0; }
语言c++计算机程序设计爱好者
不定期更新题目题解
望互相分享心得体会
有意留言加q