SAC E#1

题目背景

冴月麟和魏潇承是好朋友。

题目描述

冴月麟为了守护幻想乡,而制造了幻想乡的倒影,将真实的幻想乡封印了。任何人都无法进入真实的幻想乡了,但是她给前来救她的魏潇承留了一个线索。

她设置了一棵树(有根)。树的每一条边上具有割掉该边的代价。

魏潇承需要计算出割开这棵树的最小代价,这就是冴月麟和魏潇承约定的小秘密。

帮帮魏潇承吧。

注:所谓割开一棵有根树,就是删除若干条边,使得任何任何叶子节点和根节点不连通。

输入输出格式

输入格式:

输入第一行两个整数n,S表示树的节点个数和根。

接下来n-1行每行三个整数a、b、c,表示a、b之间有一条代价为c的边。

输出格式:

输出包含一行,一个整数,表示所求最小代价。

思路:

好坑啊。。。空间卡的好严,用定长数组存边调了半天才卡过去

这道题很多人用的费用流,但我不会

很多人用的邻接表,但我比较懒

这道题其实我们可以这样想:

因为这是一棵树,所以我们可以想到树形DP

我们从根节点往下走,一直到叶子节点

因为要求的是割掉所有子结点,那么我们有两种选择

要么割掉这个子节点上连的边

要么割掉某个与他祖先连的边

于是,我们的DP数组表示的就是在i点时,割掉与他所辖的所有的叶子节点的最小代价

怎么转移呢?

我们知道从一个祖先点往下,如果想要割掉他的所有子树所辖的所有子节点

要么割掉他与那个子树相连那条边

要么我们割掉与子树相连的所有非返祖边

这样就是一个DP

每次枚举子树

要么割掉边,要么割掉子树

取最小代价

一层层递归向上

根节点就是答案

代码:

#include<iostream>
#include<cstdio>
#define rii register int i
using namespace std;
struct bian{
    int to[70],sl;
    long long val[70];
}x[100001];
long long dp[100001],n,root;
void dplast(int wz,int fa)
{
    if(x[wz].sl==0)
    {
        return;
    }
    for(rii=1;i<=x[wz].sl;i++)
    {
        if(x[wz].to[i]!=fa)
        {
            dplast(x[wz].to[i],wz);
            dp[wz]+=min(x[wz].val[i],dp[x[wz].to[i]]);//更新答案,要么加上这条边,要么加上子节点以下的删除代价
        }
    }
}
int main()
{
    scanf("%lld%lld",&n,&root);
    for(rii=1;i<=n-1;i++)
    {
        long long ltt,kkk,val;
        scanf("%lld%lld%lld",&ltt,&kkk,&val);
        x[ltt].sl++;
        x[ltt].to[x[ltt].sl]=kkk;
        x[ltt].val[x[ltt].sl]=val;
        x[kkk].sl++;
        x[kkk].to[x[kkk].sl]=ltt;
        x[kkk].val[x[kkk].sl]=val;
    }
    for(rii=1;i<=n;i++)
    {
        if(x[i].sl==1&&i!=root)
        {
            dp[i]=1<<25;//初始化,因为不可能割掉叶子结点的子节点(不存在),所以赋值为无穷大(这里我设成1<<25)
        }
    }
    dplast(root,0);
    cout<<dp[root];
}
原文地址:https://www.cnblogs.com/ztz11/p/9289691.html