SOJ 1219. 新红黑树

解题技巧:

  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 }

 

posted @ 2016-01-09 10:57  MchCyLh  阅读(441)  评论(0编辑  收藏  举报