HDU 6201 transaction transaction transaction(树形dp)

来源:互联网 发布:mac开发工具 编辑:程序博客网 时间:2024/06/07 20:50

http://acm.hdu.edu.cn/showproblem.php?pid=6201
和那个HDU 2196 Computer(树形DP) 应该差不多的吧。。

题意:给你一颗树,边有权值,现在有本书,每个点都有这本书的价格,现在你可以选任意一点作为起点买这本书,跑去任意一点卖掉,过程中消耗路径的权值花费,问你任选起点和终点最大收益是多少

看了代码;这个dp【0】【u】表示最小的价格,然后初始是买,所以就可以打到只买一次。。不过理论上可能MLE。但是没有。。应该要每次都清空当前的S

#include<iostream>#include<cstring>#include<string>#include<cstdio>#include<algorithm>#include<vector>#include<queue>using namespace std;const int maxn = 200005;typedef long long ll;const int inf = 0x3f3f3f3f;int f[maxn];int pc[maxn];int p[maxn];int dp[2][maxn];struct edge {    int to;    int cost;    edge(int to, int cost) :to(to), cost(cost) {}};struct node {    int id;    int md;    bool operator <(const node& s) const {        return md < s.md;    }};vector<edge> G[maxn];vector<node> S[maxn];void init(int n) {    for (int i = 1; i <= n; i++) {        G[i].clear();        S[i].clear();    }    memset(dp, 0, sizeof(dp));}void dfs(int u, int fa,int c) {    p[u] = c;    f[u] = fa;    dp[0][u] = pc[u];    for (int i = 0; i < G[u].size(); i++) {        edge e = G[u][i];        if (e.to == fa) continue;        int v = e.to;        dfs(v, u,e.cost);        node now;        now.id = v;        now.md = dp[0][v]+e.cost;        S[u].push_back(now);    }    if (S[u].empty()) return;    sort(S[u].begin(), S[u].end());    dp[0][u] = min(dp[0][u],S[u][0].md);}void dfs2(int u, int fa) {    dp[1][u] = pc[u];    if (fa != -1) {        if (S[fa][0].id != u) {            dp[1][u] = min(dp[1][u],min(dp[1][fa], dp[0][fa]) + p[u]);        } else {            if (S[fa].size() == 1) dp[1][u] = min(dp[1][u],dp[1][fa] + p[u]);            else dp[1][u] = min(dp[1][u],min(S[fa][1].md, dp[1][fa])+p[u]);        }    }    for (int i = 0; i < G[u].size(); i++) {        int v = G[u][i].to;        if (v == fa) continue;        dfs2(v, u);    }}int main() {    int T;    scanf("%d",&T);    while(T--) {        int n;        scanf("%d",&n);        init(n);        for(int i=1;i<=n;i++){            scanf("%d",&pc[i]);        }        for (int i = 2; i <= n; i++) {            int x,y,z;            scanf("%d%d%d",&x,&y,&z);            edge e(y,z);            G[x].push_back(e);            e.to=x;            G[y].push_back(e);        }        dfs(1, -1, 0);        dfs2(1, -1);        int ans=0;        for (int i = 1; i <= n; i++) {            int sum=min(dp[0][i],dp[1][i]);            ans=max(ans,pc[i]-sum);        }        printf("%d\n",ans);    }    return 0;}

还看到别人的这种做法,代码又短。又好。看来状态的定义很关键。。
参考http://blog.csdn.net/cillyb/article/details/77924055

dp[u][0]表示以u为根的子树中买一本书的最大收益,dp[u][1]表示以u为根的子树中卖一本书的最大收益。dp[u][0]+dp[u][1]即为在以u为根的子树中选两点的最大收益

心得:自己真的很少见到这样的方式。。。而且这个初始化。。。%
这样子把买和卖分开了,而且不用到父亲和儿子。。。。方式挺好的。

void dfs(int u, int pre)  {      dp[u][0] = -a[u];      dp[u][1] = a[u];      for(int i = 0; i < g[u].size(); i++)      {          int v = g[u][i].v;          int w = g[u][i].w;          if(v == pre) continue;          dfs(v, u);          dp[u][0] = max(dp[u][0], dp[v][0]-w);          dp[u][1] = max(dp[u][1], dp[v][1]-w);      }      ans = max(ans, dp[u][0]+dp[u][1]);  }  
阅读全文
0 0