POJ-1947 Rebuilding Roads 树形DP

来源:互联网 发布:雕刻机钻孔编程方法 编辑:程序博客网 时间:2024/05/30 02:24

树形DP的第一题吧,前几道都是根据树形结构求解。

给出N个节点,N-1条边,要求获得一棵p个节点的树最少需要断掉多少边。

对于每一个节点的子树有舍弃这个子树,获得一部分两种操作。

 dp[s][i] = min (min(dp[s][j]+dp[k][i-j]) ,  dp[s][i]+1 )

#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <queue>#include <algorithm>#include <iomanip>#define inf (1<<29)using namespace std;int n,p,root;int son[2000];int father[200];int brother[200];int dp[200][200];//以第i个节点为根时有j个节点需要断掉的最小的边 void dfs(int root)//搜索以某个点为根时的结果 {int k,temp;for(int i=0;i<=p;i++){dp[root][i]=inf;} dp[root][1]=0;//到叶子节点时刚好为1. k=son[root];//对子树进行选取 while(k){dfs(k);for(int i=p;i>=1;i--){temp=dp[root][i]+1;//舍弃这棵子树 for(int j=1;j<i;j++){temp=min(temp,dp[k][i-j]+dp[root][j]);//不舍弃这棵子树 }dp[root][i]=temp;//以root为根的i个节点的树最小需要断掉的根 }k=brother[k];//下一棵子树 }}int solve(){int ans;dfs(root);ans=dp[root][p];//以根节点为根 for(int i=1;i<=n;i++){ans=min(ans,dp[i][p]+1);//以其余节点为根要+1 }return ans;}int main(){int u,v;while(scanf("%d%d",&n,&p)!=EOF){memset(father,0,sizeof(father));memset(son,0,sizeof(son));for(int i=1;i<n;i++){scanf("%d%d",&u,&v);father[v]=1;brother[v]=son[u];son[u]=v;}for(int i=1;i<=n;i++){if(!father[i]){root=i;break;}}printf("%d\n",solve());}return 0;}


0 0