标签:
解题技巧:
1.输入的树可能不是根据从根节点到子节点的顺序输入的。
例如:
输入可能是:
3
1 0 1 100
2 1 -1 200
2 3 1 400
所以一个较笨的方法是:先存成矩阵的形式,再转换成邻接链表的形式。
2.接着是一个记忆化搜索的过程。因为树枝数n<=20,即节点数<=21,故可以借助位运算的技巧,将一个树的状态压缩成一个整数。
树的状态是指树中哪些节点是有效可访问的。
dp[i][j]记录的是树的状态为i时,j先剪树的使得D满足j的期望的最优值.对剪红树枝的A来说,D越大越好;对剪黑树枝的B来说,D越小越好。
3.注意在计算dp[i][j]时,如果当前j不能剪树枝,则证明状态为i的树中,所有树枝都是另外一种颜色(j不能剪的颜色)。
代码如下:
1 #include <cstdio> 2 #include <vector> 3 #include <queue> 4 #include <climits> 5 #include <algorithm> 6 using namespace std; 7 8 struct Edge { 9 int to; 10 int color; // color = 0 -> red | color = 1 -> black 11 int weight; 12 Edge(int t = 0, int c = 0, int w = 0) : to(t), color(c), weight(w) {} 13 }; 14 15 struct Attribute { 16 bool used; 17 int color; 18 int weight; 19 }; 20 21 const int maxn = 20; 22 const int states = 1 << 21; 23 const int INF = INT_MAX; 24 Attribute m[maxn + 1][maxn + 1]; 25 26 vector<Edge> matrix[maxn + 1]; 27 28 bool visited[maxn + 1]; 29 int n; 30 31 int dp[states][2]; // dp[i][j] 表示状态为i的树j先走的D的最大值 32 int subtree[maxn + 1]; // 子树的状态 33 34 void init() { 35 for (int i = 0; i < maxn + 1; ++i) { 36 for (int j = 0; j < maxn + 1; ++j) 37 m[i][j].used = false; 38 matrix[i].clear(); 39 visited[i] = false; 40 subtree[i] = 0; 41 } 42 for (int i = 0; i < states; ++i) dp[i][0] = dp[i][1] = INF; 43 dp[1][0] = dp[1][1] = 0; 44 } 45 46 void transform(int cur) { 47 visited[cur] = true; 48 vector<Edge> &vt = matrix[cur]; 49 for (int i = 0; i <= n; ++i) { 50 if (!visited[i] && m[cur][i].used) { 51 vt.push_back(Edge(i, m[cur][i].color, m[cur][i].weight)); 52 transform(i); 53 } 54 } 55 } 56 57 int subTree(int root) { 58 vector<Edge> &vt = matrix[root]; 59 int state = 1 << root; 60 for (int i = 0; i < vt.size(); ++i) { 61 Edge &e = vt[i]; 62 state += subTree(e.to); 63 } 64 return subtree[root] = state; 65 } 66 67 int DP(int state, int color) { 68 if (dp[state][color] != INF) return dp[state][color]; 69 70 if (color == 0) dp[state][color] = -INF; 71 else dp[state][color] = INF; 72 73 int tot[2] = { 0, 0 }; 74 bool canCut = false; 75 76 queue<int> que; 77 que.push(0); 78 while (!que.empty()) { 79 int cur = que.front(); que.pop(); 80 vector<Edge> &vt = matrix[cur]; 81 for (int i = 0; i < vt.size(); ++i) { 82 Edge &e = vt[i]; 83 if (((1 << e.to) & state) == 0) continue; 84 que.push(e.to); 85 if (e.color == color) { 86 canCut = true; // 表示需要修剪的颜色为color时,可以找到可剪的树枝 87 int weight = (color == 0 ? e.weight : -e.weight); 88 int st = subtree[e.to]; // 包括e.to 89 int next_state = state & (~st); 90 int next_color = (color + 1) % 2; 91 if (color == 0) { 92 dp[state][color] = max(dp[state][color], DP(next_state, next_color) + weight); 93 } else { 94 dp[state][color] = min(dp[state][color], DP(next_state, next_color) + weight); 95 } 96 } 97 tot[e.color] += (e.color == 0 ? e.weight : -e.weight); 98 } 99 } 100 if (!canCut) dp[state][color] = tot[(color + 1) % 2]; // 如果全部都是另外一种情况,则另外一种情况必然全选 101 return dp[state][color]; 102 } 103 104 int main() { 105 while (scanf("%d", &n) != EOF) { 106 init(); 107 108 // 读入新红黑树 109 int node1, node2, color, weight; 110 for (int i = 0; i < n; ++i) { 111 scanf("%d%d%d%d", &node1, &node2, &color, &weight); 112 color = color == -1 ? 1 : 0; // color = 0 -> red | color = 1 -> black 113 m[node1][node2].used = m[node2][node1].used = true; 114 m[node1][node2].color = m[node2][node1].color = color; 115 m[node1][node2].weight = m[node2][node1].weight = weight; 116 } 117 118 // 转换新红黑树->邻接链表 119 transform(0); 120 // 构造每个子树的状态 121 subTree(0); 122 123 // 使用算法查找红黑树状态为(111...111),A均(砍红树)现行的D的最大值 124 int state = (1 << (n + 1)) - 1; 125 printf("%d\n", DP(state, 0)); 126 } 127 return 0; 128 }
标签:
原文地址:http://www.cnblogs.com/mchcylh/p/5115629.html