Suzhou Adventure 树形dp

1 篇文章 0 订阅
1 篇文章 0 订阅

问题描述

描述 Little Hi is taking an adventure in Suzhou now. There are N
beautiful villages in Suzhou which are numbered from 1 to N. They
connected by N-1 roads in such a way that there is excactly one way to
travel from one village to another. Little Hi gives each village a
score according to its attractiveness. He is visiting village 1 now
and plans to visit excatly M villages (including village 1) and maxize
the total score of visited villages. Further more, K villages are
recommended by Little Ho. He does not want to miss these recommended
villages no matter what their attractiveness scores are.

Note that Little Hi visits every village on his travel route. Passing
a village without visiting it is not allowed. Please find the maximum
total score Little Hi can get.

输入 The first line contains 3 integers N(1 <= N <= 100), K(1 <= K <= 5)
and M(1 <= M <= N), representing the number of villages, the number of
recommended villages and the number of villages Little Hi plans to
visit. The second line contains N integers, the attractiveness scores
of villages. The scores are between 1 to 100. The third line contains
K integers, the list of recommended villages. The following N-1 lines
each contain two integers a and b, indicating that village a and
village b are connected by a road.

输出 The maximum scores Little Hi can get. If there is no solution
output -1.

问题分析

题目中可以分析出结果必须满足
1, 要选择子树内的节点,必须选择root节点
2, K个节点时必须选择的
首先考虑第一个条件,我们可以设计状态变量f[i][j] 表示在以i为root的子树最多选用j个节点时,可以取得的最大值。
则状态转移公式为
f[i][j] = 0 if not chose i
= w[i] + max{以i为根的子树中选择j个节点取得的} if chose i
这里 max{以i为根的子树中选择j个节点取得的} 的求解等价于一个泛化背包问题。设计状态变量g[i][j] 表示前i个子节点为根的子树中选择了j个节点,所取得的最大值,那么转移公式为
g[i][j] = max{g[i-1][j-k] + f[i][k]} 0<= k <= j

对于第二个条件,我们设置一个must数组,并将从根到所有必须访问节点的路径上的所有节点置为1, 然后统计must数组中的个数,如果大于M则无解,对于必须选择的节点,我们可以将其权重值加上一个很大的数MOD,这样得到的结果中肯定含有这些节点,最后输出结果时对结果取MOD的模。

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
enum {maxn = 100+5};
int f[maxn][maxn];
int w[maxn];
bool must[maxn];
vector<int> recomm;
int father[maxn];
vector<int> tree[maxn];
int N, K, M;
int MOD = 100000;
void findFather(int now, int f)
{
    father[now] = f;
    for (int i=0;i< tree[now].size(); i++)
    {
        if (tree[now][i] != f)
            findFather(tree[now][i], now);
    }
}
void setMust()
{
    for (int i=0; i< recomm.size(); i++)
    {
        int j=recomm[i];
        while(j>0)
        {
            must[j] = true;
            j= father[j];
        }
    }
}
int dp(int rt, int m)
{
    if (m <=0)
        return 0;
    if (f[rt][m] != -1)
        return f[rt][m];
    // do not chose rt;
    f[rt][m] = 0;


    int g[2][m];
    memset(g, 0, sizeof(g));
    // chose rt;
    int a = 0;
    for(int i=0; i< tree[rt].size();i++)
    {
        if (tree[rt][i] == father[rt])
            continue;
       for (int j=0; j<= m-1; j++)
       {
           g[a][j] = 0;
           for (int k=0; k<=j; k++)
                if (g[!a][j-k] + dp(tree[rt][i], k) > g[a][j])
                    g[a][j] = g[!a][j-k] + dp(tree[rt][i], k);
       }
       a= !a;
    }
    int chose = g[!a][m-1] + w[rt];
    if (must[rt])
        chose += MOD;
    if (f[rt][m] < chose)
        f[rt][m] = chose;
    return f[rt][m];

}
int main()
{
    //freopen("in.txt", "r", stdin);
    memset(must, 0, sizeof(must));
    memset(father, 0, sizeof(father));
    memset(f, -1, sizeof(f));
    scanf("%d %d %d", &N, &K, &M);
    for (int i=1; i<= N; i++)
        scanf("%d", &w[i]);
    must[1] = true;
    for (int i=0; i< K; i++)
    {
        int a;
        scanf("%d", &a);
        must[a] = true;
        recomm.push_back(a);
    }
    for (int i=0; i< N-1; i++)
    {
        int a, b;
        scanf("%d %d", &a, &b);
        tree[a].push_back(b);
        tree[b].push_back(a);
    }
    findFather(1, 0);
    setMust();
    int cnt =0;
    for (int i=1; i<=N; i++)
    {
        if (must[i])
            cnt++;
    }
    if (cnt > M)
        printf("-1\n", cnt);
    else
        printf("%d\n", dp(1, M)%MOD);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值