树形DP集锦2

一共有n(<=200)个国家, 你想要至少收买m(<=m)个国家,其中收买国家i的代价是w[ i ]。但是有些国家有拥属关系,如果A拥属B国,就是买通了A也意味着买通了B,而且这些关系是传递的。求最少需要付出的代价。(poj)

读入有点意思。

const  int maxn = 208  ;
int  n , m ;
vector<int> g[maxn] ;
int  w[maxn] ;
int  dp[maxn][maxn] , son[maxn] ;
void  dfs(int u){
      son[u] = 1 ;
      dp[u][0] = 0 ;
      for(int i =  0 ;  i < g[u].size() ; i++){
           int v = g[u][i] ;
           dfs(v) ;
           for(int k = n ; k >= 0 ; k--){
               for(int j = 0 ; j <= k ; j++)
                   dp[u][k] = min(dp[u][k] , dp[u][k-j] + dp[v][j]) ;
           }
           son[u] += son[v] ;
      }
      dp[u][son[u]] = min(dp[u][son[u]] , w[u]) ;
}

int  in[maxn]  ;
char str[1008] ;
map<string , int> h ;

int  main(){
     int  i , id , u , v ;
     while(gets(str)){
          if(str[0] == '#') break ;
          sscanf(str , "%d%d" ,&n ,&m) ;
          for(i = 0 ; i <= n ; i++) g[i].clear()  ;
          h.clear() ;
          id = 0 ;
          memset(in , 0 , sizeof(in)) ;
          memset(son , 0 , sizeof(son)) ;
          memset(dp , 63 , sizeof(dp)) ; //最大值inf
          for(i = 1 ; i <= n ; i++){
               scanf("%s" ,str) ;
               if(h.find(str) == h.end()) h[str] = ++id ;
               u = h[str] ;
               scanf("%d" ,&w[u]) ;
               gets(str) ;
               stringstream s(str) ;
               string name ;
               while(s>>name){
                    if(h.find(name) == h.end())  h[name] = ++id ;
                    v = h[name] ;
                    g[u].push_back(v) ;
                    in[v]++ ;
               }
          }
          for(i = 1 ; i <= n ; i++){
               if(in[i]==0) g[0].push_back(i) ;
          }
          dfs(0) ;
          int s = dp[0][m] ;
          for(i = m ; i <= n ; i++)  s = min(s , dp[0][i]) ;
          printf("%d\n" , s) ;
     }
     return 0 ;
}

给出一个大小为n的树,几每个节点的权值。问这棵树中大小m的子树最大权值。(zoj)

const int  maxn = 108 ;
vector<int> g[maxn] ;
int   dp[maxn][maxn] ;
int   n , m ;
bool  vis[maxn] ;

void  dfs(int u){
      vis[u] = 1 ;
      for(int i = 0 ; i < g[u].size() ; i++){
           int v = g[u][i] ;
           if(vis[v]) continue ;
           dfs(v) ;
           for(int j = m ; j > 1 ; j--){
               for(int k = 1 ; k < j ; k++)
                   dp[u][j] = max(dp[u][j] , dp[u][j-k] + dp[v][k]) ;
           }
      }
}

int  main(){
     int i , u , v  ;
     while(scanf("%d%d" ,&n ,&m) != EOF){
          for(i = 0 ; i <= n ; i++) g[i].clear() ;
          memset(vis , 0 , sizeof(vis)) ;
          memset(dp , 0 , sizeof(dp)) ;
          for(i = 0 ; i < n ; i++) scanf("%d" ,&dp[i][1]) ;
          for(i = 1 ; i < n ; i++){
               scanf("%d%d" ,&u ,&v) ;
               g[u].push_back(v) ;
               g[v].push_back(u) ;
          }
          int s = 0 ;
          dfs(0) ;
          for(i = 0 ; i < n ; i++) s = max(s , dp[i][m]) ;
          printf("%d\n" ,s) ;
     }
     return 0 ;
}

给出一棵树,问最少切断几条边可以得到有p个结点的子树。(poj)

dp[i][j]表示以第i个节点为根的子树保留j个节点最少需要去掉几条边
const int  maxn = 158 ;
vector<int> g[maxn] ;
int  dp[maxn][maxn] ;
int  m  ;

void  dfs(int u , int father){
      dp[u][1] = 0 ;
      for(int i = 0 ; i < g[u].size() ; i++){
           int v = g[u][i] ;
           if(v == father) continue  ;
           dfs(v , u) ;
           for(int j = m ; j >= 1 ; j--){
               dp[u][j] = dp[u][j] + 1 ;
               for(int k = 1 ; k < j ; k++)
                   dp[u][j] = min(dp[u][j] , dp[u][j-k] + dp[v][k]) ;
           }
      }
}

int  main(){
     int i , n , u , v  , s ;
     while(cin>>n>>m){
          for(i = 1 ; i <= n ; i++) g[i].clear() ;
          for(i = 1 ; i < n ; i++){
               scanf("%d%d" ,&u ,&v) ;
               g[u].push_back(v) ;
               g[v].push_back(u) ;
          }
          memset(dp , 63 , sizeof(dp)) ;
          dfs(1, -1) ;
          s = dp[1][m] ;
          for(i = 2 ; i <= n ; i++) s = min(s , dp[i][m] + 1) ;
          printf("%d\n" ,s) ;
     }
     return 0 ;
}

树,给每个结点的value 和 weight 。求容量为m的背包,能获得的最大value。选择了父结点才能选择子结点。m = 0时 ,输出0 。

const  int  maxn = 108 ;
vector<int> g[maxn] ;
int    w[maxn] , val[maxn] ;
int    dp[maxn][maxn] ;
int    m ;

void   dfs(int u , int father){
       for(int i = w[u] ; i <= m ; i++) dp[u][i] = val[u] ;
       for(int e = 0 ; e < g[u].size() ; e++){
           int v = g[u][e] ;
           if(v == father)  continue  ;
           dfs(v , u)  ;
           for(int i = m ; i >= w[u] ; i--){
               for(int j = 1 ; i-j >= w[u] ; j ++) //必须选父亲
                   dp[u][i] = max( dp[u][i] , dp[u][i-j]+dp[v][j] ) ;
           }
       }
}

int   main(){
      int n  , i , u , v  ;
      while(cin>>n>>m){
           if(n==-1 && m==-1)  break ;
           for(i = 1 ; i <= n ; i++) g[i].clear() ;
           for(i = 1 ; i <= n ; i++){
                scanf("%d%d" ,&w[i] , &val[i]) ;
                w[i] = (w[i]+19)/20  ;
           }
           for(i = 1 ; i < n ; i++){
                scanf("%d%d" ,&u ,&v) ;
                g[u].push_back(v) ;
                g[v].push_back(u) ;
           }
           if(m == 0){puts("0") ; continue  ; }
           memset(dp , 0 , sizeof(dp)) ;
           dfs(1 , -1) ;
           printf("%d\n" , dp[1][m]) ;
      }
      return 0 ;
}





你可能感兴趣的:(树形DP集锦2)