CodeForces 581F Contest Page 【树形dp】

F. Zublicanes and Mumocrates
time limit per test
3 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output

It's election time in Berland. The favorites are of course parties of zublicanes and mumocrates. The election campaigns of both parties include numerous demonstrations on n main squares of the capital of Berland. Each of the n squares certainly can have demonstrations of only one party, otherwise it could lead to riots. On the other hand, both parties have applied to host a huge number of demonstrations, so that on all squares demonstrations must be held. Now the capital management will distribute the area between the two parties.

Some pairs of squares are connected by (n - 1) bidirectional roads such that between any pair of squares there is a unique way to get from one square to another. Some squares are on the outskirts of the capital meaning that they are connected by a road with only one other square, such squares are called dead end squares.

The mayor of the capital instructed to distribute all the squares between the parties so that the dead end squares had the same number of demonstrations of the first and the second party. It is guaranteed that the number of dead end squares of the city is even.

To prevent possible conflicts between the zublicanes and the mumocrates it was decided to minimize the number of roads connecting the squares with the distinct parties. You, as a developer of the department of distributing squares, should determine this smallest number.

Input

The first line of the input contains a single integer n (2 ≤ n ≤ 5000) — the number of squares in the capital of Berland.

Next n - 1 lines contain the pairs of integers x, y (1 ≤ x, y ≤ n, x ≠ y) — the numbers of the squares connected by the road. All squares are numbered with integers from 1 to n. It is guaranteed that the number of dead end squares of the city is even.

Output

Print a single number — the minimum number of roads connecting the squares with demonstrations of different parties.

Sample test(s)
input
8
1 4
2 4
3 4
6 5
7 5
8 5
4 5
output
1
input
5
1 2
1 3
1 4
1 5
output
2

这次是纯纯的树型dp了,好难

题意:把给定的树的叶子节点分成两半,使得同一拨的叶子节点不能直接或者间接相连,问至少需要删多少条边

借用前辈的思路就是:siz[u]是以u为节点的子树节点个数

dp[u][0][i]表示以u为根节点的子树把i个叶子节点分出去,其中不包括节点u时需要删的最少边数;

dp[u][1][i]表示以u为根节点的子树把i个叶子节点分出去,其中包括节点u时需要删的最少边数

既然说要研究叶子节点个数,我就要看看谁是叶子节点,度是1的点肯定可以看做是叶子节点(不要纠结于“根"节点也可以度为1的问题,把他掰下去就得了啊)找一个度不是1的作为根,dfs的时候使用。重点说说dfs的过程~

既然是树型dp一定要考虑初始化的问题,当此点是非叶子结点时dp[u][0][0] = dp[u][1][0] = 0无论是否包括此点,只有把0个点分出去删边数已知为0;当此点是叶子节点时dp[u][0][1]=dp[u][1][0] = 0,把一个节点不包括自己分出去需要删0条边,把0个节点包括自己分出去需要删0条边(这里好抽象)

递归过程:由于这个题是与节点个数有关的,for循环依旧是根据这个来的,多出的c[]纯粹为了过渡==



#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
struct node
{
    int to,next;
}edge[100040];
int tol,head[5005];
int dp[5005][2][5005],c[2][5005];
int deg[5005],siz[5005];
int n;
void addedge(int u,int v)
{
    edge[tol].to=v,edge[tol].next=head[u],head[u]=tol++;
}
void dfs(int u,int pre)
{
    if(siz[u]==1)
        dp[u][0][1]=dp[u][1][0]=0;
    else
        dp[u][1][0]=dp[u][0][0]=0;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==pre)continue;
        dfs(v,u);
        for(int i=0;i<=n;i++) c[1][i]=c[0][i]=inf;
        for(int i=0;i<=siz[u];i++)
        {
            for(int j=0;j<=siz[v];j++)
            {
                c[1][i+j]=min(c[1][i+j],dp[u][1][i]+dp[v][1][j]);
                c[1][i+j]=min(c[1][i+j],dp[u][1][i]+dp[v][0][j]+1);
                c[0][i+j]=min(c[0][i+j],dp[u][0][i]+dp[v][0][j]);
                c[0][i+j]=min(c[0][i+j],dp[u][0][i]+dp[v][1][j]+1);
            }
        }
        siz[u]+=siz[v];
        for(int i=0;i<=siz[u]+siz[v];i++)
        {
            dp[u][1][i]=c[1][i];
            dp[u][0][i]=c[0][i];
        }
    }
}
int main()
{
    //freopen("cin.txt","r",stdin);
    scanf("%d",&n);
    tol=0;
    memset(head,-1,sizeof(head));
    memset(deg,0,sizeof(deg));
    for(int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        addedge(u,v);
        addedge(v,u);
        deg[u]++;deg[v]++;
    }
    int root=1;
    for(int i=1;i<=n;i++)
    {
        if(deg[i]==1)
        {
            siz[i]=1;
        }
        else
        {
            root=i;
            siz[i]=0;
        }

    }
    memset(dp,inf,sizeof(dp));
    dfs(root,-1);
    printf("%d\n",min(dp[root][1][siz[root]/2],dp[root][0][siz[root]/2]));
    return 0;
}

一种久违的补作业的既视感orz 其实7点十几分就到了(逃




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值