hdu 4714

一个树形dp的题,又是一个涉及不深的领域  = =;

不过在网上看到了大神用很巧的思路解决了这个题;

大神的思路就是:

从树的底部往上看:如果一棵子树拥有两个及以上的叶子节点,可以将这棵子树与大树分离,并且将子树化成一条直线;

为什么这样子是最优呢?我不会证明,但我觉得这种情况可以保留最多不被删除的边;

代码:

 1 #pragma comment(linker,"/STACK:1024000000,1024000000")
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<queue>
 5 #include<vector>
 6 #define maxn 1000005
 7 using namespace std;
 8 
 9 vector<int>ve[maxn];
10 bool v[maxn];
11 int ans;
12 int dfs(int a)
13 {
14     v[a]=1;
15     int l=ve[a].size(),res=0;
16     for(int i=0; i<l; i++)
17         if(!v[ve[a][i]])
18             res+=dfs(ve[a][i]);
19     if(res>=2)
20     {
21         if(a==1)ans+=res-2;
22         else ans+=res-1;
23         return 0;
24     }
25     else return 1;
26 }
27 
28 int main()
29 {
30     int t,n,a,b;
31     scanf("%d",&t);
32     while(t--)
33     {
34         ans=0;
35         scanf("%d",&n);
36         for(int i=1; i<=n; i++)
37             ve[i].clear();
38         for(int i=1; i<n; i++)
39         {
40             scanf("%d%d",&a,&b);
41             ve[a].push_back(b);
42             ve[b].push_back(a);
43         }
44         dfs(1);
45         printf("%d\n",2*ans+1);
46         memset(v,0,sizeof v);
47     }
48     return 0;
49 }
View Code

利用dp的思想:将整棵树拆成N个单枝(所有点的度小于2),所需的cost即为2*N-1(拆成N个单枝需 N-1 cost,合成一个环需要 N cost)。

  可用树形DP求出N最小的情况,用dp[i][j]表示以i为根的可用度为j的最小单枝数。
状态转移方程:dp[root][0]=min(dp[root][0]+dp[son][0],dp[root][1]+dp[son][1]-1)
       dp[root][1]=min(dp[root][1]+dp[son][0],dp[root][2]+dp[son][1]-1)
       dp[root][2]=dp[root][2]+dp[son][0]
代码:
 1 #pragma comment(linker,"/STACK:1024000000,1024000000")
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<queue>
 5 #include<vector>
 6 #define maxn 1000005
 7 using namespace std;
 8 
 9 vector<int>ve[maxn];
10 bool v[maxn];
11 int dp[maxn][3];
12 int ans;
13 
14 void dfs(int a)
15 {
16     v[a]=1;
17     dp[a][0]=dp[a][1]=dp[a][2]=1;
18     int l=ve[a].size();
19     for(int i=0;i<l;i++)
20     {
21         int u=ve[a][i];
22         if(!v[u])
23         {
24             dfs(u);
25             dp[a][0]=min(dp[a][0]+dp[u][0],dp[a][1]+dp[u][1]-1);
26             dp[a][1]=min(dp[a][1]+dp[u][0],dp[a][2]+dp[u][1]-1);
27             dp[a][2]=dp[a][2]+dp[u][0];
28         }
29     }
30 }
31 
32 int main()
33 {
34     int t,n,a,b;
35     scanf("%d",&t);
36     while(t--)
37     {
38         memset(dp,0,sizeof dp);
39         ans=0;
40         scanf("%d",&n);
41         for(int i=1; i<=n; i++)
42             ve[i].clear();
43         for(int i=1; i<n; i++)
44         {
45             scanf("%d%d",&a,&b);
46             ve[a].push_back(b);
47             ve[b].push_back(a);
48         }
49         dfs(1);
50         ans=min(dp[1][0],min(dp[1][1],dp[1][2]));
51         printf("%d\n",2*ans-1);
52         memset(v,0,sizeof v);
53     }
54     return 0;
55 }
View Code

 

posted @ 2013-09-16 15:02  Yours1103  阅读(221)  评论(0编辑  收藏  举报