树形dp

来源:互联网 发布:免费制作电子相册软件 编辑:程序博客网 时间:2024/05/16 18:01

OJ题目:click here~~

给一棵树,问至少砍掉几个树枝,能得到有m个结点的子树。

const int Max_N = 152;vector < int > List[Max_N];int n , m;int dp[Max_N][Max_N];void dfs(int u , int father){    dp[u][1] = 0;    int i , j , k;    for(i = 0;i < List[u].size();i++){        int v = List[u][i];        if(v == father) continue;        dfs(v , u);        for(j = m;j >= 1;j--){           dp[u][j]++;           for(k = 1;k < j;k++){               dp[u][j] = min(dp[u][j] , dp[v][k] + dp[u][j - k]);           }        }    }}int main(){    //freopen("in.txt","r",stdin);    while(scanf("%d%d",&n,&m) != EOF){        int a , b , i , j , k ;        for(i = 0;i <= n;i++) List[i].clear();        for(i = 1;i < n;i++){            scanf("%d%d",&a,&b);            List[a].push_back(b);            List[b].push_back(a);        }        memset(dp , 63 , sizeof(dp));        dfs(1 , -1);        int ans = dp[1][m];        for(i = 2;i <= n;i++)            ans = min(ans , dp[i][m] + 1);        cout << ans << endl;    }}

OJ题目:click here~~

用代价W[ i ]贿赂父亲结点,即可拥有该父亲结点,所有的子结点 和 子结点的子结点…… 求拥有m个结点所需要的最小代价。

const int maxn = 202;int n , m;map<string , int> h;vector<int> g[maxn];int in[maxn];int w[maxn];int dp[maxn][maxn];int son[maxn];void dfs(int u){    dp[u][0] = 0;//取0个结点的时候花费是0    son[u]++;//加入u自己    int i , j , k , v;    for(i = 0;i < g[u].size();i++){//遍历u的子树        v = g[u][i];        dfs(v);        for(j = n;j >= 0;j--){//树上的分组背包            for(k = 0;k <= j;k++){                dp[u][j] = min(dp[u][j] , dp[v][k] + dp[u][j - k]);            }        }        son[u] += son[v];//加上子树上的结点数    }    dp[u][son[u]] = min(dp[u][son[u]] , w[u]);//如果选取了u本身,则花费为w(u)}int main(){    char s[200000];    //freopen("in.txt","r",stdin);    while(gets(s)){        int i , j , k , c , id = 1 , u , v;//id从1开始,后面将增加一个虚拟结点0,将森林化为树。        if(s[0] == '#') break;        sscanf(s , "%d%d" , &n ,&m);//这里s必须为char数组        for(i = 0 ;i <= n;i++) g[i].clear();        h.clear();        memset(in , 0 , sizeof(in));        memset(son , 0 , sizeof(son));        memset(dp , 63 , sizeof(dp));        for(i = 1;i <= n;i++){            scanf("%s",s);            if(h.find(s) == h.end()) h[s] = id++;            u = h[s];            scanf("%d",&w[u]);            gets(s);            stringstream str(s);            string name;            while(str >> 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 ans = dp[0][m];        for(i = m;i <= n;i++)            ans = min(ans , dp[0][i]);        printf("%d\n",ans);    }    return 0;}

OJ题目:click here~~

给出一棵树上每个结点的权值,找有m个结点的子树的权值和最大值。

const int maxn = 102;int n , m;vector<int> g[maxn];int visit[maxn];int dp[maxn][maxn];void dfs(int u , int father){    int i  , j , k;    for(i = 0;i < g[u].size();i++){        int v = g[u][i];        if(v == father)continue;        dfs(v , u);        for(j = m;j > 1;j--){            for(k = 1;k < j;k++){                dp[u][j] = max(dp[u][j] , dp[v][k] + dp[u][j - k]);            }        }    }}int main(){    int i , j , k , a , b;    //freopen("in.txt","r",stdin);    while(scanf("%d%d",&n,&m) != EOF){        for(i = 0;i < n;i++) g[i].clear();        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",&a,&b);            g[a].push_back(b);            g[b].push_back(a);        }        dfs(0 , -1);        int ans = 0;        for(i = 0;i < n;i++)            ans = max(ans , dp[i][m]);        cout << ans << endl;    }}

OJ题目:click here~~

树上的01背包,选过父结点才能选自结点。

const int maxn = 102;int val[maxn];int w[maxn];vector<int> g[maxn];int dp[maxn][maxn];int n , m ;void dfs(int u , int father){    int v , i , j , k;    for(i = w[u];i <= m;i++) dp[u][i] = val[u];    for(i = 0;i < g[u].size();i++){        v = g[u][i];        if(v == father) continue;        dfs(v , u);        for(j = m;j >= w[u];j--){            for(k = 1;k + j <= m;k++){                dp[u][j + k] = max(dp[u][j + k] , dp[v][k] + dp[u][j]);            }        }    }}int main(){    int i , j , k ,a , b;    //freopen("in.txt" , "r" , stdin);    while(scanf("%d%d",&n,&m)){        if(n == -1 && m == -1) break;        memset(dp , 0 , sizeof(dp));        for(i = 0 ;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", &a , &b);            g[a].push_back(b);            g[b].push_back(a);        }        if(m == 0) {puts("0") ; continue;}//没有士兵时,不能获得任何东西,即使有bug = 0的结点。        dfs(1 , -1);        cout << dp[1][m] << endl;    }}


0 0