Codeforces Gym100502G:Outing(缩点+有依赖的树形背包)

http://codeforces.com/gym/100502/attachments

题意:有n个点,容量为tol,接下来n个关系,表示选了第i个点,那么第xi个点就必须被选。问最多可以选多少个点使得不超过容量tol。

思路:由题目样例可得,边可能出现自环的情况,这个时候这条边其实没用。然后因为是一个图,所以需要缩点,缩完之后用一个sz数组表示点的大小,重新建一幅图。因为有可能是森林,所以需要添加一个虚根,使得其变成一棵树。然后题目就转变为求有依赖的树上背包了。

我是学了这篇博客的写法:http://blog.csdn.net/y990041769/article/details/38068223

dp[i][j]表示以i为根的子树在容量为j的时候能放的点的最大个数。sz[i]表示i结点的大小。

因为i是必须选的(不然其儿子都没办法选),那么一开始就直接赋予其值。

1         for(int j = tol; j >= sz[u]; j--) { // 枚举容量
2             for(int k = 0; k <= j - sz[u]; k++) { // k表示能分配给子节点的容量
3                 dp[u][j] = max(dp[u][j], dp[u][j-k] + dp[v][k]);
4             }
5         }

接下来第一层循环是像01背包一样,枚举能够放下u(根结点)自己的容量,第二层循环k代表能够分配给儿子的容量,因为根结点自己已经放进去了,所以是j-sz[u]。就这样可以更新完u这个根节点了。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define N 1010
 4 struct Edge {
 5     int v, nxt;
 6 } edge[N*2], e[N*2];
 7 int n, tol, head[N], tot, h[N], t, dfn[N], low[N], belong[N], vis[N], num, tid, sz[N], deg[N], dp[N][N];
 8 stack<int> sta;
 9 
10 void Add(int u, int v) { edge[tot] = (Edge) {v, head[u]}; head[u] = tot++; }
11 void add(int u, int v) { e[t] = (Edge) {v, h[u]}; h[u] = t++; }
12 
13 void tarjan(int u) {
14     sta.push(u);
15     vis[u] = 1;
16     dfn[u] = low[u] = ++tid;
17     for(int i = head[u]; ~i; i = edge[i].nxt) {
18         int v = edge[i].v;
19         if(!dfn[v]) {
20             tarjan(v);
21             low[u] = min(low[u], low[v]);
22         } else if(vis[v]) {
23             low[u] = min(low[u], dfn[v]);
24         }
25     }
26     if(low[u] == dfn[u]) {
27         num++;
28         int top = -1;
29         while(top != u) {
30             top = sta.top(); sta.pop();
31             belong[top] = num;
32             sz[num]++;
33             vis[top] = 0;
34         }
35     }
36 }
37 
38 void BuildGraph() {
39     memset(head, -1, sizeof(head));
40     memset(h, -1, sizeof(h));
41     t = tot = 0;
42     for(int i = 1; i <= n; i++) {
43         int j; scanf("%d", &j);
44         if(i == j) continue;
45         Add(j, i);
46     }
47     for(int i = 1; i <= n; i++)
48         if(!dfn[i]) tarjan(i);
49 
50     // 将环缩点重新建图
51     for(int u = 1; u <= n; u++) {
52         for(int i = head[u]; ~i; i = edge[i].nxt) {
53             int v = edge[i].v;
54             if(belong[u] != belong[v]) {
55                 add(belong[u], belong[v]);
56                 deg[belong[v]]++;
57             }
58         }
59     }
60     for(int i = 1; i <= num; i++) // 设一个虚根将森林变成树
61         if(!deg[i]) add(0, i);
62 }
63 
64 void dfs(int u) {
65     for(int i = tol; i >= sz[u]; i--) dp[u][i] = sz[u]; // 初始状态必须有这个结点的sz
66     for(int i = h[u]; ~i; i = e[i].nxt) {
67         int v = e[i].v;
68         dfs(v);
69         for(int j = tol; j >= sz[u]; j--) { // 枚举容量
70             for(int k = 0; k <= j - sz[u]; k++) { // k表示能分配给子节点的容量
71                 dp[u][j] = max(dp[u][j], dp[u][j-k] + dp[v][k]);
72             }
73         }
74     }
75 }
76 
77 void Bag() {
78     dfs(0);
79     printf("%d
", dp[0][tol]);
80 }
81 
82 int main() {
83     scanf("%d%d", &n, &tol);
84     BuildGraph();
85     Bag();
86     return 0;
87 }
原文地址:https://www.cnblogs.com/fightfordream/p/6522447.html