poj-1947 Rebuilding Roads

来源:互联网 发布:淘宝怎么预防死人衣服 编辑:程序博客网 时间:2024/06/07 07:20

题意:

给出一颗树,剪去一些边将它变成一个含有m个结点的树;

求减去的最小边数;

1<=m<=n<=150;


题解:

显然是树形dp,考虑状态要包括当前结点信息和子树大小;

就设状态f[ x ][ j ]为在以x为根的子树上,取包括x的j个点,所需要剪掉的边数;

对于x的子结点y,深搜回溯之后的转移为:

f[ x ][ j ]=min(f[ x ][ j-k ]+f[ y ][ k ]);

        0<=k<=size[y]

然而在x有多个子结点的时候会造成前一个dp值过小而无法转移;

这时候应该将f[ x ][ j ]+1之后再转移,因为相对于前一个,剪边数最多+1(即将新结点剪掉);

这道题的状态还是很好想的,就是边界和转移比较坑;

还有最后的答案除了根结点以外,都要+1,因为要从父结点上剪掉这个树;


代码:


#include<vector>#include<stdio.h>#include<string.h>#include<algorithm>#define N 155using namespace std;vector<int>to[N];int size[N],f[N][N],n,m,ans;void dfs(int x,int pre){size[x]=1;f[x][0]=1;f[x][1]=0;int i,j,k,y;for(i=0;i<to[x].size();i++){if((y=to[x][i])!=pre){dfs(y,x);size[x]+=size[y];for(j=size[x];j>=1;j--){f[x][j]++;for(k=0;k<=size[y]&&k<j;k++){f[x][j]=min(f[x][j-k]+f[y][k],f[x][j]);}}}}if(size[x]>=m)ans=min(ans,f[x][m]+1);}int main(){int i,j,k,x,y;scanf("%d%d",&n,&m);for(i=1;i<n;i++){scanf("%d%d",&x,&y);to[x].push_back(y);to[y].push_back(x);}ans=0x3f3f3f3f;memset(f,0x3f,sizeof(f));dfs(1,0);ans=min(ans,f[1][m]);printf("%d",ans);return 0;}



0 0