Luogu P1272 Problem Solving Report

P1272 Rebuilding the road

Topic description

After a terrible earthquake, the farmer \(John\) 's pasture was rebuilt with \(N\) barns \((1≤N≤150\) , numbered \(1..N\) ). The road from one barn to another barn is now the only one as people don't have time to build extra roads. Therefore, the pasture transportation system can be constructed as a tree. \(John\) Want to know how much damage another earthquake will cause. Some roads, once destroyed, will separate a subtree containing \(P(1≤P≤N)\) barns from the remaining barns, and \(John\) wants to know the minimum number of these roads.

Input and output format

Input format:

Line 1: 2 integers, \(N\) and \(P\)

Line \(2..N\) : 2 integers \(I\) and \(J\) in each line , indicating that node \(I\) is the parent node of node \(J\) .

Output format:

A single line containing the minimum number of paths that, if destroyed, would separate a subtree of exactly \(P\) nodes.


I've been very depressed recently, and this fairly bare tree DP has also stuck me for a long time.


First make it clear(stuck me for a long time), this is a tree, the root can be chosen at will.

If you only enter the tree according to the title, it will be large (however, the data water is only wrong by two points)


Let \(dp[i][j]\) represent the minimum number of edges cut off when the subtree whose root node is \(i\) loses (note that it loses) \(j\) points.

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

  • Note: This one has been rolled out of one dimension (the number of subtrees) , and it must be upside down when enumerating \(j\)

I have a question, what should I do if I cut off my son directly?

I did this in the beginning, every time I enumerate \(j\)

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

However, doing so is repetitive. (think about it)

There is a very meow (wonderful) approach.

Just cut herself off every time \(dfs\) finishes seeking \(i\) -> \(dp[i][size[i]]=1\) ;

Going back to the beginning, although the root is uncertain, in fact, you only need to take a random root and pull it.

For example , \(root\) is the root, \(dp[root][np]\) certainly does not include the case of cutting herself

We are in other nodes, cut ourselves into a tree, that is, update \(ans\) with \(dp[i][p-(n-size[i])]+1\ )

#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\n",ans);
    return 0;
}

2018.5.6

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325345596&siteId=291194637