codeforces581f(树形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,用dp[i][j]表示节点i删除j个叶子节点所需要的最短的步数


说明:(望大家先看完并且看懂代码)

代码里面标注的必须逆序的原因:

先做一个图:


现在我们来做对于4的dp部分,假设已经对4做完了1的dp以及dfs部分,正在对2做dp部分。

如果是顺序,我们将要得到的dp顺序是:dp(4,0)(4,1)(4,2)(4,3)(4,4)

然后更新得到dp(4,1)(4,2)(4,3)(4,4)(4,5)

接着得到dp(4,2)(4,3)(4,4)(4,5)(4,6)

最后得到dp(4,3)(4,4)(4,5)(4,6)(4,7)


现在将眼光放到4,7(举个特例)上:dp[4][3+4]=dp[4][3]+dp[2][4];到这里看起来的确没问题,可是我们要明确这里的dp值是否真正的正确,当然dp[4][3]和dp[2][4]是没问题咯,然而正是因为dp[4][3]的没问题导致了dp[4][7]的有问题!!!为什么?因为dp[4][3+4]=dp[4][3]+dp[2][4];的数值恰好只有一种情况满足这个条件,就是刚好把1的所有节点删除和把2的所有节点删除,然而它放在这里是错的,因为在它之前更新dp[4][3]已经更新了好几遍了,相当于可能(这只是打比方啊!不代表我举的例子真的就是错的)dp[4][3]对应的数值已经代表删除1的一个节点再删除2的2个节点了,如果合并,相当于1中的剩下两个节点没有被删除,而2中的2个节点被分别多删了1次,可是这显然是不现实的,相当于直接把整个图都改变了,当然会错!可是反过来,我们用逆序,得到的dp顺序是:

dp(4,3)(4,4)(4,5)(4,6)(4,7)

然后更新得到dp(4,2)(4,3)(4,4)(4,5)(4,6)

接着得到dp(4,1)(4,2)(4,3)(4,4)(4,5)

最后得到dp(4,0)(4,1)(4,2)(4,3)(4,4)

首先我们就计算了dp[4][3+0]=dp[4][3]+dp[2][0]:意义显然得知,由于dp[4][3]未得到过更新,相当于就是左子树1全部删除节点再加上右子树没有删除一个节点。

然后dp[4][3+1]=dp[4][3]+dp[2][1],和上面一样,正是由于dp[4][3]没有得到更新,所以它更新比它大的dp值才会有意义,这里还是左子树1全部删除,然后右子树2删除一个。

依次类推……

dp[4][3+4]=dp[4][3]+dp[2][4];到了这里,由于dp[4][3]并没有更新,所以相当于左边1全部删除+右边2全部删除,当然结果就对了~!



#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
using namespace std;
typedef long long ll;
const int N = 5001,INF=999999999;
vector<int> tu[N];
int z[N];///某个节点的叶子节点数
int dp[N][N];///dp[i][j]表示叶子节点i删除j个节点所需要的最短的步数
int n;

void dfs(int now,int pre)
{
    if(tu[now].size()==1)
        z[now]=1;
    else z[now]=0;
    dp[now][0]=0;
    for(int i=1; i<=n; i++)
        dp[now][i]=INF;
    int l=tu[now].size();
    for(int i=0; i<l; i++)
    {
        int next=tu[now][i];
        if(next==pre)continue;
        dfs(next,now);
        for(int j=z[now]; j>=0; j--)///必须逆序
            for(int k=0; k<=z[next]; k++)
                dp[now][j+k]=min(dp[now][j+k],dp[now][j]+dp[next][k]);///状态转移
        z[now]+=z[next];
    }
    for(int i=0; i<=z[now]; i++)
        dp[now][i]=min(dp[now][i],dp[now][z[now]-i]+1);///把now节点算进去,删除i个叶节点相当于一刀把now节点连接父亲的边删掉(所有叶节点删掉之后,相当于代码+1操作)再把z[now]-i个本来不打算删掉的叶节点删回来
}

int main()
{
    scanf("%d",&n);
    if(n==2)
    {
        puts("1");
        return 0;
    }
    memset(dp,0,sizeof(dp));
    for(int i=0; i<n-1; i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        tu[a].push_back(b);
        tu[b].push_back(a);
    }
    int root=1;
    while(tu[root++].size()==1);
    dfs(root,0);
    printf("%d\n",dp[root][z[root]/2]);
}



posted @ 2016-03-30 21:05  martinue  阅读(296)  评论(0编辑  收藏  举报