1001_Capturing_a_country

来源:互联网 发布:淘宝店怎么改店名 编辑:程序博客网 时间:2021/01/23 05:17

题目:

hdu_4340_Capturing_a_country

官方题解:

树dp。
显然题中给图的是一颗树。问题可以抽象成对树的每个点都染色,有两中颜色可以选择。
我们可以知道,如果某一个连通的点集染的是同一种颜色,则这个集合中只要而且必须有一个点取完整的费用,其他的点都只需要对应费用的一半。
状态:dp[i][j][k]  (0 <= i <= n,  0<=j<=1, 0 <= k <= 1) 表示以i为根的子树的费用,其中i节点被染成了第j种颜色,且子树中与i染成同一种颜色的与i连通的点集有k个点选取了完整的费用。
若选取1为根节点,则最后需要的结果为: min(dp[1][0][1], dp[1][1][1]}。
状态转移方程:
v为i节点的儿子节点。令 S = sum{min(dp[v][j][0], dp[v][1-j][1])}, det = min{dp[v][j][1] - min(dp[v][j][0], dp[v][1-j][1])}; 
dp[i][j][0] = cost[i][j]/2 + S;  
dp[i][j][1] = min(cost[i][j] + S, cost[i][j]/2 + S + det);


个人理解:

要注意的是并不是相邻的点被染色该点才能被染色,所以答案唯一能确保的性质是染上同一种颜色的连通分量中仅有一个点去完整费用。所以对于已染上某色的点有两种情况,一是该点为根的子树中与根同色的连同分量中没任何一点取完整费用,二是有一点去完整费用。这样就保证了无后效性。另外还要注意的是,枚举状态时可用循环。

标程:
#include <cstdio>#include <algorithm>#include <cstring>#include <iostream>#include <cmath>#include <vector>#include <queue>#include <string>#include <set>#include <map>//#include <ctime>using namespace std;typedef long long ll;const int N = 111;const int INF = 0x3f3f3f3f;int ar[2][N];int n;vector<int> adj[N];int dp[N][3][3];bool vis[N];int dfs(int cur, int a, int b, int fa) {    //cout << cur << endl;    int &res = dp[cur][a][b];    if (res != -1) return res;    if (adj[cur].size() == 1 && adj[cur][0] == fa) {        if (b == 1) res = ar[a][cur];        else res = ar[a][cur] / 2;    } else {        for (int i = 0; i < adj[cur].size(); ++i) if (adj[cur][i] != fa) {                dfs(adj[cur][i], 0, 0, cur);                dfs(adj[cur][i], 0, 1, cur);                dfs(adj[cur][i], 1, 0, cur);                dfs(adj[cur][i], 1, 1, cur);            }        if (b == 1) {            int sum = 0;            int v, temp;            int minv = INF;            for (int i = 0; i < adj[cur].size(); ++i) {                v = adj[cur][i];                if (v != fa) {                    temp = min(dp[v][a][0], dp[v][1 - a][1]);                    sum += temp;                    minv = min(minv, dp[v][a][1] - temp);                }            }            // if (cur == 1) cout << sum << " " << minv << endl;            res = min(ar[a][cur] + sum, ar[a][cur] / 2 + sum + minv);        } else {            res = ar[a][cur] / 2;            int v;            for (int i = 0; i < adj[cur].size(); ++i) {                v = adj[cur][i];                if (v != fa) {                    res += min(dp[v][a][0], dp[v][1 - a][1]);                }            }        }    }    return res;}int Ans() {    memset(dp, -1, sizeof (dp));    int ans1 = dfs(1, 0, 1, -1);    int ans2 = dfs(1, 1, 1, -1);    //cout << ans1 << " " << ans2 << endl;    return min(ans1, ans2);}int main() { //   freopen("in", "r", stdin);//    freopen("out", "w", stdout);    while (cin >> n) {        for (int i = 1; i <= n; ++i) {            scanf("%d", &ar[0][i]);        }        for (int j = 1; j <= n; ++j) {            scanf("%d", &ar[1][j]);        }        for (int i = 1; i <= n; ++i) adj[i].clear();        int a, b;        for (int i = 1; i < n; ++i) {            scanf("%d%d", &a, &b);            adj[a].push_back(b);            adj[b].push_back(a);        }        printf("%d\n", Ans());    }}

原创粉丝点击