题意:给定一个有n个节点的无根树,有两种装置A和B,每种都有无限多个。在某个节点X使用A装置需要C1的花费,并且此时与节点X相连的边都被覆盖。在某个节点X使用B装置需要C2的花费,并且此时与节点X相连的边以及与X相连的点相连的边都被覆盖。求覆盖所有边的最小花费。
析:树形DP,这是一个比较难想的,
dp[i][0] 表示 在 i 为根的子树不安装,且其子树的边都覆盖,
dp[i][1] 表示 在 i 处安装装置A
dp[i][2] 表示 在 i 处安装装置B
dp[i][3] 表示 在 i 为根的子树不安装,对于子结点不作要求,但对孙结点是要覆盖的
状态转移方程,第二个不好转移,其他的都好说
dp[i][0] += min(dp[v][1], dp[v][2]);
dp[i][2] += min(min(dp[v][0], min(dp[v][1], dp[v][2])), dp[v][3]) + C2;
dp[i][3] += min(dp[v][0], min(dp[v][1], dp[v][2]));
对于dp[i][1] 有两种转移方式,第一种,在 i 处真正的安装装置A,那么就是
dp[i][0] += min(dp[v][1], dp[v][2]);
还有一种,那就是它的子结点至少有一个安装装置B,那么就相当于 i 结点安装了装置A。
dp[i][2] += min(min(dp[v][0], min(dp[v][1], dp[v][2])), dp[v][3]) + det。
其中det表示最小的一个值在其子结点中安装装置B的花费。
代码如下:
#pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #include <cstdlib> #include <cmath> #include <iostream> #include <cstring> #include <set> #include <queue> #include <algorithm> #include <vector> #include <map> #include <cctype> #include <cmath> #include <stack> #include <sstream> #include <list> #include <assert.h> #include <bitset> #define debug() puts("++++"); #define gcd(a, b) __gcd(a, b) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define fi first #define se second #define pb push_back #define sqr(x) ((x)*(x)) #define ms(a,b) memset(a, b, sizeof a) #define sz size() #define pu push_up #define pd push_down #define cl clear() #define all 1,n,1 #define FOR(x,n) for(int i = (x); i < (n); ++i) #define freopenr freopen("in.txt", "r", stdin) #define freopenw freopen("out.txt", "w", stdout) using namespace std; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> P; const int INF = 0x3f3f3f3f; const double inf = 1e20; const double PI = acos(-1.0); const double eps = 1e-8; const int maxn = 10000 + 10; const int mod = 1000; const int dr[] = {-1, 0, 1, 0}; const int dc[] = {0, 1, 0, -1}; const char *de[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"}; int n, m; const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; inline bool is_in(int r, int c){ return r > 0 && r <= n && c > 0 && c <= m; } int c1, c2; struct Edge{ int to, next; }; Edge edge[maxn<<1]; int cnt, head[maxn]; int dp[maxn][4]; void addEdge(int u, int v){ edge[cnt].to = v; edge[cnt].next = head[u]; head[u] = cnt++; } void dfs(int u, int fa){ dp[u][0] = dp[u][3] = 0;; dp[u][1] = c1; dp[u][2] = c2; int mmin = INF, sum = 0; for(int i = head[u]; ~i; i = edge[i].next){ int v = edge[i].to; if(v == fa) continue; dfs(v, u); dp[u][0] += min(dp[v][1], dp[v][2]); int t = min(dp[v][0], min(dp[v][1], dp[v][2])); dp[u][1] += t; dp[u][2] += min(t, dp[v][3]); dp[u][3] += t; sum += t; mmin = min(mmin, dp[v][2] - t); } sum += mmin; dp[u][1] = min(dp[u][1], sum); } int main(){ while(scanf("%d %d %d", &n, &c1, &c2) == 3 && n){ cnt = 0; ms(head, -1); for(int i = 1; i < n; ++i){ int u, v; scanf("%d %d", &u, &v); addEdge(u, v); addEdge(v, u); } dfs(1, -1); printf("%d\n", min(dp[1][0], min(dp[1][1], dp[1][2]))); } return 0; }