hihocoder1104(Dfs预处理+树形DP)

部署运行你感兴趣的模型镜像

题目描述

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.

算法思路

从题目中来看,我们发现这是一棵树,而且根据经过就必须观光的条件。我们可以进行第一次剪枝,剪枝过后所有的叶子结点都是重要的节点。很显然,这棵子树是我们必须遍历的。并且记录下在原树中与这棵子树直接相连的树的节点。
下一步,对所有记录下的节点进行树形DP,在遍历过剩下的这棵树之后就可以直接使用背包来进行扩展了,那么最后的最优解很显然我们也十分容易来构造。

ans=dp[mi]+sum(i)

其中i是第一次我们求得的子树的节点的数量,很显然,在这里问题是存在可能无解的,比如这棵子树的节点数量就超过了预期要游览的节点数量,那么就会无解,或者总的节点数量比想要游览的节点数量小,那么也是无解的(不过这里不可能)。否则必须存在解。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;

#define MAXN 105

int attr[MAXN],siz[MAXN];
vector<int>grid[MAXN];
vector<int>id;
bool isImportant[MAXN];
int n,k,m;
int dp[MAXN][MAXN];
int tongji[MAXN];

void Dfs1(int cur,int fa)
{//construct the subtree that we must visit
    int i,j;
    siz[cur] = 1;
//  printf("%d %d\n",cur,fa);

    for(i=0;i<grid[cur].size();i++){
        int dest = grid[cur][i];
        if(dest!=fa){
            Dfs1(dest,cur);
            isImportant[cur] |= isImportant[dest];
        }
    }

    if(isImportant[cur]){
        for(i=0;i<grid[cur].size();i++){
            int dest = grid[cur][i];
            if(dest!=fa){
                if(isImportant[dest]){
                    siz[cur] += siz[dest];
                    attr[cur] += attr[dest];
                }
                else
                    id.push_back(dest);
            }
        }
    }
    return;
}

void Dfs2(int cur,int fa)
{//construct the best answer via dynamic programming
    int i,j,k;
    siz[cur]=1;
    dp[cur][1] = attr[cur];

    for(i=0;i<grid[cur].size();i++){
        int dest = grid[cur][i];
        if(!isImportant[dest]&&dest!=fa){
            Dfs2(dest,cur);
            siz[cur] += siz[dest];
            for(j=siz[cur];j>1;j--){
                for(k=j-1;k>=0;k--){
                    dp[cur][j] = max(dp[cur][j],dp[cur][j-k]+dp[dest][k]);
                }
            }
        }
    }
    return;
}

int main()
{
    //freopen("input","r",stdin);
    int i,j,k;
    int tmp1,tmp2;

    memset(isImportant,false,sizeof(isImportant));
    memset(dp,0,sizeof(dp));
    memset(tongji,0,sizeof(tongji));

    scanf("%d%d%d",&n,&k,&m);

    for(i=1;i<=n;i++)
        scanf("%d",&attr[i]);

    for(i=0;i<k;i++){
        scanf("%d",&tmp1);
        isImportant[tmp1] = true;
    }

    for(i=0;i<n-1;i++){
        scanf("%d%d",&tmp1,&tmp2);
        grid[tmp1].push_back(tmp2);
        grid[tmp2].push_back(tmp1);
    }



    Dfs1(1,-1);

    if(n<m||siz[1]>m)
        printf("-1\n");
    else{
        m -= siz[1];
        for(i=0;i<id.size();i++){
            Dfs2(id[i],-1);
            for(j=m;j>=0;j--){
                for(k=j;k>=0;k--){
                    tongji[j] = max(tongji[j],tongji[k]+dp[id[i]][j-k]);
                }
            }
        }
        //printf("%d\n",attr[1]);
        printf("%d\n",attr[1]+tongji[m]);
    }
    return 0;
}

您可能感兴趣的与本文相关的镜像

Qwen-Image-Edit-2509

Qwen-Image-Edit-2509

图片编辑
Qwen

Qwen-Image-Edit-2509 是阿里巴巴通义千问团队于2025年9月发布的最新图像编辑AI模型,主要支持多图编辑,包括“人物+人物”、“人物+商品”等组合玩法

### 数位动态规划(Digit DP)与预处理统计满足条件的数 要统计满足 $S(x^2) = S(x)^2$ 的整数 $x$ 在区间 $[L, R]$ 内的数量,可以采用数位动态规划(Digit DP)结合预处理的方法。这种方法的核心思想是将问题拆解为两个部分:计算从 0 到 $R$ 的满足条件数的数量,减去从 0 到 $L-1$ 的满足条件数的数量,从而得到区间 $[L, R]$ 内的结果 [^1]。 --- ### 数位 DP 的基本结构 数位 DP 通常以递归形式实现,使用记忆化搜索技术避免重复计算。每个状态通常包括以下参数: - `pos`:当前处理到的数位位置。 - `sum_x`:当前数字的数位和。 - `sum_x2`:当前数字平方后的数位和。 - `limit`:是否受到当前位数字的限制(即是否为原数字的前缀)。 由于需要判断 $S(x^2) = S(x)^2$,可以将 `sum_x` 和 `sum_x2` 作为状态的一部分进行递归处理 [^2]。 --- ### 状态转移与剪枝优化 在递归过程中,尝试每一位可能的数字,并计算对应的平方数位和。为了避免重复计算,可以使用记忆化缓存(如 `dp[pos][sum_x][sum_x2]`)记录已处理的状态。 此外,可以利用以下数学性质进行剪枝: - 若当前 `sum_x2 > sum_x * sum_x`,则可以直接剪枝,因为后续无法再满足等式。 - 若当前 `sum_x2 < sum_x * sum_x`,可以继续递归,直到满足条件或超出范围。 --- ### 代码实现示例 ```cpp #include <bits/stdc++.h> using namespace std; int dp[20][100][100]; // dp[pos][sum_x][sum_x2] vector<int> digits; int dfs(int pos, int sum_x, int sum_x2, bool limit) { if (pos == digits.size()) { return (sum_x2 == sum_x * sum_x); } if (!limit && dp[pos][sum_x][sum_x2] != -1) { return dp[pos][sum_x][sum_x2]; } int up = limit ? digits[pos] : 9; int res = 0; for (int i = 0; i <= up; ++i) { int new_sum_x = sum_x + i; int new_sum_x2 = sum_x2 + (i * i); // 平方数位和(暂不考虑进位) res += dfs(pos + 1, new_sum_x, new_sum_x2, limit && (i == up)); } if (!limit) { dp[pos][sum_x][sum_x2] = res; } return res; } int solve(int x) { digits.clear(); while (x > 0) { digits.push_back(x % 10); x /= 10; } reverse(digits.begin(), digits.end()); memset(dp, -1, sizeof(dp)); return dfs(0, 0, 0, true); } int main() { int L, R; cin >> L >> R; int ans = solve(R) - solve(L - 1); cout << ans << endl; return 0; } ``` 该代码使用递归函数 `dfs` 处理每一位数字,并通过 `solve` 函数将问题拆解为两个部分进行计算 [^3]。 --- ### 数学性质与进位问题 在上述代码中,`new_sum_x2` 仅计算了每一位数字的平方和,未考虑进位的影响。为了更精确地处理进位,可以在递归过程中模拟数字的平方运算,并逐位计算平方后的数位和 [^4]。 一种优化方法是预先生成所有可能的数字组合(如只包含 0~3 的数字),因为这些数字在平方时不容易产生进位,从而更容易满足 $S(x^2) = S(x)^2$ 的条件。 --- ### 预处理与优化策略 为了进一步提升性能,可以预处理所有满足条件的数字,并在查询时直接统计区间内的数量。预处理方法包括: - 生成所有只包含 0~3 的数字组合。 - 对每个组合计算其平方,并判断是否满足 $S(x^2) = S(x)^2$。 - 将满足条件的数字存入集合中,用于快速查询。 --- ### 总结 通过数位动态规划结合预处理,可以高效地统计满足 $S(x^2) = S(x)^2$ 的数字数量。该方法适用于大范围的输入,并通过剪枝和数学优化减少不必要的计算。实际应用中,还可以结合进位模拟和数字组合生成策略,进一步提升效率 [^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值