题目背景
冴月麟和魏潇承是好朋友。
题目描述
冴月麟为了守护幻想乡,而制造了幻想乡的倒影,将真实的幻想乡封印了。任何人都无法进入真实的幻想乡了,但是她给前来救她的魏潇承留了一个线索。
她设置了一棵树(有根)。树的每一条边上具有割掉该边的代价。
魏潇承需要计算出割开这棵树的最小代价,这就是冴月麟和魏潇承约定的小秘密。
帮帮魏潇承吧。
注:所谓割开一棵有根树,就是删除若干条边,使得任何任何叶子节点和根节点不连通。
输入输出格式
输入格式:
输入第一行两个整数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",<t,&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]; }