zoukankan      html  css  js  c++  java
  • 洛谷 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)个节点的子树的道路的最小数目。


    最近很颓,这个还算裸的树形DP也卡了我好长时间。


    首先明确一点(卡了我好久),这是一颗树,根是可以随便选的。

    如果只按照题目把树输入是会大的(然而数据水只错了两个点)


    (dp[i][j])代表根节点为(i)的子树在失去(注意是失去)(j)个点时 切掉的边的最小个数。

    (dp[i][j]=min(dp[i][j],dp[i][j-k]+dp[i_{son}][k]))

    • 注意:这个已经滚动掉了一维(第几个子树) 枚举(j)时要倒着哦

    有个问题,对于直接切掉自己儿子的情况怎么办?

    最开始时我这样做了,在每次枚举(j)的时候

    (dp[i][j]=min(dp[i][j],dp[i][j-size[i_{son}]]+1);)

    然而这样做是重复了的。(想想看)

    有个很喵(妙)的做法。

    (dfs)每次求完(i)时把她自己切掉就好了啦 -> (dp[i][size[i]]=1);

    回到最开始,虽然根不确定,其实只需要随便取一种根的情况就行拉。

    比如说(root)是根,(dp[root][n-p])肯定不包含切她自己的情况

    我们就在其他节点中,把自己切成一棵树,即用(dp[i][p-(n-size[i])]+1)更新(ans)

    #include <cstdio>
    #include <cstring>
    int min(int x,int y) {return x<y?x:y;}
    const int N=156;
    int dp[N][N];//子树i舍弃j个点的最小切割数
    int n,p,root;
    int g[N][N],used[N],size[N];
    
    void dfs(int now)
    {
        dp[now][0]=0;
        for(int i=1;i<=n;i++)//枚举子树
            if(g[now][i])
            {
                dfs(i);
                size[now]+=size[i];
                int r=min(p,size[now]);
                for(int j=r;j>=1;j--)//枚举当前舍弃节点数
                {
                    int r2=min(min(p,size[i]),j);
                    for(int k=1;k<=r2;k++)//枚举子树状态
                        dp[now][j]=min(dp[now][j],dp[now][j-k]+dp[i][k]);
                }
            }
        dp[now][++size[now]]=1;
    }
    
    int main()
    {
        memset(dp,0x3f,sizeof(dp));
        scanf("%d%d",&n,&p);
        int u,v;
        p=n-p;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            g[u][v]=1;
            used[v]=1;
        }
        for(int i=1;i<=n;i++)
            if(!used[i])
            {
                root=i;
                break;
            }
        dfs(root);
        int ans=0x3f3f3f3f;
        for(int i=1;i<=n;i++)
        {
            if(i==root) {ans=min(ans,dp[i][p]);continue;}
            int tt=p+size[i]-n;//还要砍得
            if(tt>=0) ans=min(ans,dp[i][tt]+1);
        }
        printf("%d
    ",ans);
        return 0;
    }
    

    2018.5.6

  • 相关阅读:
    Java成神之路技术整理(长期更新)
    WEB攻击手段及防御第1篇-XSS
    JSON是什么,为什么这么流行?
    常用的 7 款 MySQL 客户端工具,你值得拥有!
    Spring的核心思想,总结得非常好!
    到底什么级别才算是高并发?
    非常详尽的 Shiro 架构解析
    7 种 JVM 垃圾收集器,看完我跪了。。
    电商那些年,我摸爬打滚出的高并发架构实战干货
    Spring Boot 把 Maven 干掉了,拥抱 Gradle!
  • 原文地址:https://www.cnblogs.com/butterflydew/p/8998703.html
Copyright © 2011-2022 走看看