• P1272 重建道路


    P1272 重建道路

    题目描述

    - 一场可怕的地震后,人们用N个牲口棚(1≤N≤150,编号1..N)重建了农夫John的牧场。
    由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是  惟一的。
    因此,牧场运输系统可以被构建成一棵树。John想要知道另一次地震会造成多严重的破坏。
    有些道路一旦被毁坏,就会使一棵含有P(1≤P≤N)个牲口棚的子树和剩余的牲口棚分离,John想知道这些道路的最小数目。
    

    输入格式

    - 第1行:2个整数,N和P
    第2..N行:每行2个整数I和J,表示节点I是节点J的父节点。
    

    输出格式

    - 单独一行,包含一旦被破坏将分离出恰含P个节点的子树的道路的最小数目。
    

    输入 #1

    - 11 6
      1 2
      1 3
      1 4
      1 5
      2 6
      2 7
      2 8
      4 9
      4 10
      4 11
    

    输出 #1

    - 2
    

    说明/提示

    - 【样例解释】
    如果道路1-4和1-5被破坏,含有节点(1,2,3,6,7,8)的子树将被分离出来
    

    题意描述

    • 有一棵树
    • 删掉一些节点
    • 使剩下的节点仍为一棵树,并恰好等于P;
    • 求删的最小次数

    主要思路:树形DP

    • 首先输入整棵多叉树
    • 然后将多叉树转为二叉树(方便计算)
    • 求删掉该节点后,会有多少节点挂掉
    • 最后将二叉树转成答案
    • 遍历二叉树 DP+记忆化 求解

    如何 DP

    • 设 dp( i , j )为在节点i删j个节点的最小次数
    • 对于节点i,有删和不删两种情况
    • 如果删,则
    	dp( i , j ) = dp( t [ i ].r , j - t [  i ].d ) + 1
    

    意为: 在右子树上删去(要删的 j 减去已经删的 t [ i ].d )节点的最小次数加上本次删的次数 1

    • 如果删, 还有一种特殊情况
    	if(m==n-p)	dp( i , j )=dp(t[ k ].l,t[ k ].d-p)+1;
    

    意为: 若其他子树还未删任何节点,可以在删去的子树中删去(子树的节点减去要保留的节点),即等同于不删子树,而删去除该子树外的所有节点

    • 如果不删,则
    	dp( i , j )=dp(t [ i ].l , k )+dp(t [ i ].r ,m-k )
    

    意为: 节点i左子树删 k 个节点,右子树删 m-k 个节 点的最小次数和

    • 加上记忆化

    t [ i ].r为右子树,t [ i ].l为左子树
    t [ i ].d为删掉该节点后,会有多少节点挂掉。
    

    传朕旨意,宣代码

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    using namespace std;//流线型头文件
    
    const int INF=9999999;
    struct tree{ int d,l,r; }t[301];
    int f[301][301];
    int n,p;//定义树
    
    int dp(int k,int m)
    {
    	if(f[k][m]>0)		return f[k][m];
    	if(m==0)		return 0;//删0个节点次数为0
    	if(m<0)			return INF;//不可能删负数个节点
    	if(k==0)		return INF;//不存在的节点
    	int tmp;
    	f[k][m]=INF;
    	tmp=dp(t[k].r,m-t[k].d)+1;
    	if(tmp<f[k][m])	f[k][m]=tmp;
    	if(m==n-p)	tmp=dp(t[k].l,t[k].d-p)+1;
    	if(tmp<f[k][m])	f[k][m]=tmp;
    	int lv,rv;
    	for(int i=0; i<=m; i++)
    	{
    		lv=dp(t[k].l,i);
    		if(lv>=INF)	continue;
    		rv=dp(t[k].r,m-i);
    		if(rv>=INF)	continue;
    		if(lv+rv<f[k][m])	f[k][m]=lv+rv;
    	}
    	return f[k][m];
    }//树形DP+记忆化
    
    void dfs(int x)
    {
    	if(x==0)	return;
    	if(t[x].l==0)
    	{
    		t[x].d=1;
    		return;
    	}
    	int r=t[x].l;
    	t[x].d+=1;
    	while(r>0)
    	{
    		dfs(r);
    		t[x].d+=t[r].d;
    		r=t[r].r;
    	}
    }//遍历树,求删掉该节点后,会有多少节点挂掉
    
    int main()
    {
    	scanf("%d%d",&n,&p);
    	int x,y,r;
    	memset(t,0,sizeof(t));
    	memset(f,-1,sizeof(f));
    	for(int i=1; i<n; i++)
    	{
    		scanf("%d%d",&x,&y);
    		if(t[x].l==0)	t[x].l=y;
    		else
    		{
    			r=t[x].l;
    			while(t[r].r>0)	r=t[r].r;
    			t[r].r=y;
    		}//转二叉树
    	}
    	dfs(1);
    	cout<<dp(1,n-p)<<endl;
    	return 0;
    }
    
  • 相关阅读:
    centos7 install flash player
    【转载】网站配置Https证书系列(二):IIS服务器给网站配置Https证书
    【转载】网站配置Https证书系列(一):腾讯云申请免费的SSL证书的流程步骤(即https安全连接使用的证书)
    【转载】网站配置Https证书系列(三):IIS网站设置Http链接直接跳转Https安全连接
    【转载】 C#使用Union方法求两个List集合的并集数据
    【转载】程序设计过程中SQL语句Where 1=1的作用
    【转载】C#编程中两个List集合使用Intersect方法求交集
    【转载】腾讯云服务器域名解析操作详解
    【转载】C#中使用OrderBy和ThenBy等方法对List集合进行排序
    【转载】腾讯云服务器通过设置安全组放行对外端口号
  • 原文地址:https://www.cnblogs.com/vasairg/p/12318496.html
Copyright © 2020-2023  润新知