现在的位置: 首页 > 综合 > 正文

POJ – 1947(树形dp)

2019年04月03日 ⁄ 综合 ⁄ 共 1289字 ⁄ 字号 评论关闭

依然典型的dp背包问题;不同之处需要说明一下;

第一点状态转移方程原方程为  

d[ i ][ j ]   = min( d[ i ][ j ] , d[ i ][ j - k ] + d[ p ][ k-1 ]  ); 其中的状态含义从根i出发i节点已经选取,还要在选取j个点的最小删除边代价; p为i节点的任一子节点;

要注意到形成的大小为m的子树,一定可以在给定的树上找到子树根节点,并且该子树与子树根节点联通;所以这是后来这样设计状态的依据;

若设d [ i ][ j ] 为从i出发i未选取, 还要在选取j个点的最小删除边代价;在状态转移时,状态转移式子为:

d[ i ][ j ]   = min( d[ i ][ j ] , d[ i ][ j - k ] + d[ p ][ k ] ); 但注意k必须小于j,原因很简单,如果那样会导致i与该子节点不连通;

还有在dfs实现时候,第一开始认为该该根节点树没有子节点,然后逐步添加;

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int inf = 1000000;
const int maxn = 155;
int len,head[maxn];
struct node{
int to,next;
}tree[maxn*2];
void add_eadge(int u,int v){
  tree[len].to = v;
  tree[len].next = head[u];
  head[u] = len++;
}
void init(){
  memset(head,-1,sizeof(head));
  len = 0;
}
int n,m,d[maxn][maxn],father[maxn],tem[maxn];
void dfs(int u,int f){
   d[u][0] = 0;
   for(int i=1;i<=m;i++) d[u][i]=inf;
   for(int i=head[u];i!=-1;i=tree[i].next){
     if(tree[i].to==f) continue;
     int p = tree[i].to;
     dfs(p,u);
     for(int j=0;j<=m;j++) tem[j] = d[u][j];
     for(int j=0;j<=m;j++){
        d[u][j]=tem[j]+1;
        for(int k=1;k<=j;k++)
            d[u][j]=min(d[u][j],tem[j-k]+d[p][k-1]);
     }
   }
}
int main()
{
    while(scanf("%d %d",&n,&m)==2){
       init();
       memset(father,0,sizeof(father));
       for(int i=1;i<n;i++){
         int x,y;
         scanf("%d %d",&x,&y);
         add_eadge(x,y);
         father[y]=1;
       }
       int root;
       for(int i=1;i<=n;i++){
         if(!father[i]){
            root = i;
            dfs(i,-1); break;
         }
       }
       int ans = d[root][m-1];
       for(int i=1;i<=n;i++)
          ans=min(ans,d[i][m-1]+1);
       printf("%d\n",ans);
    }
    return 0;
}

抱歉!评论已关闭.