强烈推荐此题。此题应用树型DP解答。
首先明确一点,题中的环至少需要3个顶点。因此,对于树中的每个顶点,有3种状态。
f[x][0]表示以x为根的树,变成每个顶点恰好在一个环中的图,需要连的最少边数。
f[x][1]表示以x为根的树,除了根x以外,其余顶点变成每个顶点恰好在一个环中的图,需要连的最少边数。
f[x][2]表示以x为根的树,除了根x以及和根相连的一条链(算上根一共至少2个顶点)以外,其余顶点变成每个顶点恰好在一个环中的图,需要连的最少边数。
有四种状态转移(假设正在考虑的顶点是R,有k个儿子):
A.根R的所有子树自己解决(取状态0),转移到R的状态1。即R所有的儿子都变成每个顶点恰好在一个环中的图,R自己不变。


B.根R的k-1个棵树自己解决,剩下一棵子树取状态1和状态2的最小值,转移到R的状态2。剩下的那棵子树和根R就构成了长度至少为2的一条链。

C.根R的k-2棵子树自己解决,剩下两棵子树取状态1和状态2的最小值,在这两棵子树之间连一条边,转移到R的状态0。

D.根R的k-1棵子树自己解决,剩下一棵子树取状态2(子树里还剩下长度至少为2的一条链),在这棵子树和根之间连一条边,构成一个环,转移到R的状态0。


这样就完成了树型DP。


/*************************************************************************
Author: WHU_GCC
Created Time: 2007-8-30 10:44:23
File Name: pku1848.cpp
Description: 
***********************************************************************
*/

#include 
<iostream>
#include 
<vector>
using namespace std;
#define out(x) (cout << #x << ": " << x << endl)
const int maxint = 0x7FFFFFFF;
typedef 
long long int64;
const int64 maxint64 = 0x7FFFFFFFFFFFFFFFLL;
template 
<class T> void show(T a, int n) {for (int i = 0; i < n; ++i) cout << a[i] << ' '; cout << endl; }
template 
<class T> void show(T a, int r, int l) {for (int i = 0; i < r; ++i) show(a[i], l); cout << endl; }

const int maxn = 110;
const int inf = 10000;

int n;
vector 
<int> v[maxn];
int f[maxn][3];
int used[maxn];

void dfs(int now)
{
    used[now] 
= 1;
    vector 
<int> child;
    
for (int i = 0; i < v[now].size(); i++if (!used[v[now][i]])
    
{
        dfs(v[now][i]);
        child.push_back(v[now][i]);
    }

    
if (child.size() == 0)
    
{
        f[now][
0= inf;
        f[now][
1= 0;
        f[now][
2= inf;
    }

    
// case 1
    {
        
int sum = 0;
        
for (int i = 0; i < child.size(); i++)
            sum 
+= f[child[i]][0];
        f[now][
1<?= sum;
    }

    
// case 2
    for (int i = 0; i < child.size(); i++)
    
{
        
int p = min(f[child[i]][1], f[child[i]][2]);
        
int sum = 0;
        
for (int j = 0; j < child.size(); j++if (j != i)
            sum 
+= f[child[j]][0];
        f[now][
2<?= sum + p;
        f[now][
0<?= sum + f[child[i]][2+ 1;
    }

    
// case 3
    for (int i = 0; i < child.size(); i++)
        
for (int j = i + 1; j < child.size(); j++)
        
{
            
int p1 = min(f[child[i]][1], f[child[i]][2]);
            
int p2 = min(f[child[j]][1], f[child[j]][2]);
            
int sum = 0;
            
for (int k = 0; k < child.size(); k++if (k != i && k != j)
                sum 
+= f[child[k]][0];
            f[now][
0<?= sum + p1 + p2 + 1;
        }

}


int dp()
{
    
for (int i = 1; i <= n; i++)
        f[i][
0= f[i][1= f[i][2= inf;
    memset(used, 
0sizeof(used));
    dfs(
1);
    
if (f[1][0== inf)
        
return -1;
    
return f[1][0];
}


int main()
{
    
while (scanf("%d"&n) != EOF)
    
{
        
for (int i = 0; i < n - 1; i++)
        
{
            
int t1, t2;
            scanf(
"%d%d"&t1, &t2);
            v[t1].push_back(t2);
            v[t2].push_back(t1);
        }

        printf(
"%d\n", dp());
    }

    
return 0;
}
posted on 2007-08-30 21:47 Felicia 阅读(1029) 评论(4)  编辑 收藏 引用 所属分类: 动态规划
Comments

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理