[SDOI2006]保安站岗

嘟嘟嘟

 

一看就是树形dp。

刚开始我的状态dp[i][0 / 1]表示以 i 为根的子树,i 选 / 不选时的最小的经费。然后转移方程我就推不出来了,因为无法很好的表示 i 和子树的关系。比如如果 i 不选,那么 i 的子节点可以选一个也可以选多个也可以一个不选,因为 i 的儿子的儿子选了的话,i 的儿子就可以不选了。所以只用选和不选表示状态远远不够。

根据上文的思路,先想一想会出现那些情况:

1. i 选了,那么 i 的儿子们就随便。

2. i 没选,但是 i 的儿子中有的选了,也就是说 i 也被控制了。

3. i 没被控制。

那么状态就是dp[i][0 /1 /2]分别表示:

0:i 没被控制

1:i 被他爹或某个儿砸控制了

2.自己被选了

考虑转移,因为是自底向上dp的,所以只用考虑 i 和儿子 j 的关系:

1.dp[i][0]:那么只有dp[j][1]可以转移,因为如果从dp[j][0]转移的话,那么 j 就永远无法控制了,不合法;从dp[j][2]转移,那么 i 一定被控制,不符合dp[i][0]。所以dp[i][0] = Σdp[j][1]

2.dp[i][1]:则 i 至少要选一个儿子,其他儿子随便(但不能是dp[j][0],原因和上面一样),所以dp[i][1] = Σmin{dp[j][1], dp[j][2]} + dp[x][2] (x != j)

2.dp[i][2]:那么儿子们就随便了,dp[i][2] = Σmin{dp[j][0], dp[j][1], dp[j][2]} + a[i]

第二种转移需要点技巧才能达到O(n),具体看代码。

初值就是当 i 为叶子的时候,dp[i][1] = INF.

 1 #include
 2 #include
 3 #include
 4 #include
 5 #include
 6 #include
 7 #include
 8 #include
 9 #include
10 #include
11 using namespace std;
12 #define enter puts("") 
13 #define space putchar(' ')
14 #define Mem(a, x) memset(a, x, sizeof(a))
15 #define rg register
16 typedef long long ll;
17 typedef double db;
18 const int INF = 0x3f3f3f3f;
19 const db eps = 1e-8;
20 const int maxn = 1505;
21 inline ll read()
22 {
23   ll ans = 0;
24   char ch = getchar(), last = ' ';
25   while(!isdigit(ch)) last = ch, ch = getchar();
26   while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
27   if(last == '-') ans = -ans;
28   return ans;
29 }
30 inline void write(ll x)
31 {
32   if(x < 0) x = -x, putchar('-');
33   if(x >= 10) write(x / 10);
34   putchar(x % 10 + '0');
35 }
36 
37 int n, a[maxn];
38 ll dp[maxn][3];
39 struct Edge
40 {
41   int nxt, to;
42 }e[maxn << 1];
43 int head[maxn], ecnt = -1;
44 void addEdge(int x, int y)
45 {
46   e[++ecnt] = (Edge){head[x], y};
47   head[x] = ecnt;
48 }
49 
50 void dfs(int now, int f)
51 {
52   ll sum = 0;
53   for(int i = head[now]; i != -1; i = e[i].nxt)
54     {
55       int v = e[i].to;
56       if(v == f) continue;
57       dfs(v, now);
58       dp[now][0] += dp[v][1];
59       sum += min(dp[v][1], dp[v][2]);
60       dp[now][2] += min(dp[v][0], min(dp[v][1], dp[v][2]));
61     }
62   dp[now][2] += a[now]; dp[now][1] = INF;
63   for(int i = head[now]; i != -1; i = e[i].nxt)
64     {
65       int v = e[i].to;
66       if(v == f) continue;
67       dp[now][1] = min(dp[now][1], sum - min(dp[v][1], dp[v][2]) + dp[v][2]);
68     }
69 }
70 
71 int main()
72 {
73   Mem(head, -1);
74   n = read();
75   for(int i = 1; i <= n; ++i)
76     {
77       int x = read(); a[x] = read();
78       int m = read();
79       for(int j = 1; j <= m; ++j)
80     {
81       int y = read();
82       addEdge(x, y); addEdge(y, x);
83     }
84     }
85   dfs(1, 0);
86   write(min(dp[1][1], dp[1][2])), enter;
87   return 0;
88 }
View Code

 

转载于:https://www.cnblogs.com/mrclr/p/9898275.html

你可能感兴趣的:([SDOI2006]保安站岗)