P1352 没有上司的舞会

这是我从luogu讲义弄下来的题目。


简化题意:

给你一棵树,每个结点有权值,父结点参加,子结点都不能参加。求最大权值和。

讲义告诉我们这道题用dp做。

本来我想的是用一维状态,结果发现错了。。。

加一维

也许加一维,问题就能更显然地解决,这道题数据范围不大,也说明要加一维了。。。

打出状态转移方程

\(dp[i][0]\)\(dp[i][1]\)\(i\)结点不去或去的最大权值和。

如果这个点不去,那么有:

\[dp[u][0] = \sum(max(dp[v][0], dp[v][1])), (u,v) \in E \]

如果这个点去,就有:

\[dp[u][1] = weight[u] + \sum(dp[v][0]), (u,v) \in E \]

有唯一的根节点为校长,就算不告诉我们也可以\(O(n)\)的找出来,建图后找到入度为0的点就是了。

最后的答案当然是校长去或不去两种情况的最大值。

我快读没写负数结果90。。。

代码:

#include<cstdio>
#include<algorithm>
const int maxn = 6005, INF = 1e9 + 7;
struct Edges
{
    int next, to;
} e[maxn];
int head[maxn], tot;
int dp[maxn][2];
bool vis[maxn][2];
int weight[maxn];
int indegree[maxn];
int n, root;
int read()
{
    int ans = 0, s = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-') s = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        ans = ans * 10 + ch - '0';
        ch = getchar();
    }
    return s * ans;
}
void link(int u, int v)
{
    e[++tot] = (Edges){head[u], v};
    head[u] = tot;
}
int solve(int u, bool go)
{
    if(vis[u][go]) return dp[u][go];
    int ans;
    if(go)
    {
        ans = weight[u];
        for(int i = head[u]; i; i = e[i].next)
        {
            int v = e[i].to;
            ans += solve(v, 0);
        }
    }
    else
    {
        ans = 0;
        for(int i = head[u]; i; i = e[i].next)
        {
            int v = e[i].to;
            ans += std::max(solve(v, 0), solve(v, 1));
        }
    }
    vis[u][go] = true;
    return dp[u][go] = ans;
}
int main()
{
    n = read();
    for(int i = 1; i <= n; i++) weight[i] = read();
    for(int i = 1; i < n; i++)
    {
        int l = read(), k = read();
        link(k, l);
        indegree[l]++;
    }
    for(int i = 1; i <= n; i++) if(indegree[i] == 1)
    {
        root = i; break;
    }
    printf("%d\n", std::max(solve(root, 0), solve(root, 1)));
    return 0;
}

posted @ 2018-07-20 15:12  Garen-Wang  阅读(95)  评论(0)    收藏  举报