POJ 1947 Rebuilding Roads

来源:互联网 发布:java知识点重点和难点 编辑:程序博客网 时间:2024/05/17 07:07

题目大意:

给你一棵树,问要切割出节点数为m的子树,至少需要多少次切割;

解题思路:

我们考虑i结点为根结点,那么切割出j个结点需要的最少的次数;
dp[i][j]表示以i为根结点,切割出j个结点的子树(该子树包含i结点),那么对与每一个根结点,dp[i][1]我们不考虑它的父亲结点,则需要size【i】次,这里size是它的直接相连的子孙个数,注意直接相连;
对于状态dp[i][j],我们要考虑与它的子孙v的切割状态,切割掉子树得到dp【i】[j]状态,不切割子树则为dp[i][k]+dp[v][j-k]-1,为什么要减1呢?dp【i】[k]肯定不包含dp[v][j-k],如果包含的话,它的结点数就要大于k了,所以dp【i】[k】保存的状态是切掉了dp[v][j-k]的,但是现在要加上后者,所以要少切1次;
因此状态转移方程就成为了dp[i][j]=min(dp[i][j],dp[i][k]+dp[v][j-k]);
最后在找答案的时候,因为我们是按每个点为根结点考虑的,因此在取实际结果的时候,除了深搜开始的根节点,其他结点的答案都要加1,因为要切开与父亲结点的连接;
#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define maxn 200int cnt,head[maxn];int dp[maxn][maxn],size[maxn];int n,m;struct Edge{int to,next;}edge[2*maxn];void init(){cnt=0;memset(head,-1,sizeof(head));memset(dp,0x3f3f3f3f,sizeof(dp));memset(size,0,sizeof(size));} void addedge(int u,int v){edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;}void tree_dp(int u,int f){dp[u][1]=size[u];for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;if(v!=f){tree_dp(v,u);for(int j=m;j>=1;j--){for(int k=1;k<=j;k++)dp[u][j]=min(dp[u][j],dp[u][k]+dp[v][j-k]-1);}}}}int main(){while(~scanf("%d%d",&n,&m)){init();for(int i=1;i<n;i++){int a,b;scanf("%d%d",&a,&b);addedge(a,b);size[a]++;}tree_dp(1,-1);int ans=dp[1][m];for(int i=1;i<=n;i++)ans=min(ans,dp[i][m]+1);printf("%d\n",ans);}}


0 0
原创粉丝点击