1. 程式人生 > >hiho1055 - 樹形dp轉換成背包

hiho1055 - 樹形dp轉換成背包

get urn pro clas 一個 函數計算 val names include

題目鏈接

輸入:一棵樹,每個節點一個權值。

輸出:包括1號節點在內的m個節點組成的連通分量的權值和的最大值

/**********************************************/

計 dp(i,j) 為以i為根的子樹選中j個點(包括i)時的最大權值和。則dp(1,m)即為所求。

方程:

{

dp[i][0] = 0;

dp[i][1] = value[i];

foreach child c of i

for j = m...2

for k = 1...j-1

dp[i][j] = max(dp[i][j],dp[i][j-k]+dp[c][k])

}

因為dp的核心就是記憶化搜索,所以自下向上遍歷整棵樹,處理完一個節點就標記一下,下次用到這個節點的時候就不用再遞歸了。

這裏我用getCnt()函數計算了一下以每個節點i為根的子樹中節點的數目cnt(i),為的是縮小求dp(i,j)中j和k的上限,由m變為MIN(m,cnt(i)),應該不會提速多少

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <cmath>
#include <vector>
#include <string>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>

#define MAX(a,b) ((a)>=(b)?(a):(b))
#define MIN(a,b) ((a)<=(b)?(a):(b))
#define long long LL
#define OO 0x0fffffff
using namespace std;
const int N = 111;

struct Edge{
    int to;
    int next;
};
int eid = 0;
Edge edges[N*2];
int heads[N];
void addEdge(int a,int b){
     edges[eid].to = a;
     edges[eid].next = heads[b];
     heads[b] = eid++;

     edges[eid].to = b;
     edges[eid].next = heads[a];
     heads[a] = eid++;
}

int m,n;
int dp[N][N],cnt[N],visited[N];

void getCnt(int id){
    visited[id] = 1;
    for(int cur = heads[id];cur!=-1;cur=edges[cur].next){
        int cid = edges[cur].to;
        if(!visited[cid]) {
           if(!cnt[cid]) getCnt(cid);
           cnt[id] += cnt[cid];
        }
    }
    cnt[id] += 1;
}
void traverse(int id){
    visited[id] = 1;
    for(int cur=heads[id];cur!=-1;cur=edges[cur].next){
        int cid = edges[cur].to;
        if(!visited[cid]){
            if(visited[cid]!=2) traverse(cid);
            for(int i=MIN(m,cnt[id]);i>=2;i--){
                for(int j=1;j<MIN(i,cnt[cid]+1);j++){
                   dp[id][i]=MAX(dp[id][i],(dp[id][i-j]+dp[cid][j]));
                }
            }
        }
    }
    visited[id] = 2;
}
int main(){
    scanf("%d%d",&n,&m);
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++) scanf("%d",dp[i]+1);

    int a,b;
    memset(heads,-1,sizeof(heads));
    for(int i=0;i<n-1;i++){
        scanf("%d%d",&a,&b);
        addEdge(a,b);
    }

    memset(cnt,0,sizeof(cnt));
    memset(visited,0,sizeof(visited));
    getCnt(1);

    memset(visited,0,sizeof(visited));
    traverse(1);

    printf("%d\n",dp[1][m]);
    return 0;
}

hiho1055 - 樹形dp轉換成背包