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 }
利用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 }