poj 1947 树形背包 (删边)

http://blog.csdn.net/woshi250hua/article/details/7632785

这道题我一开始想的dp[i][j],i是节点,j是删除的点数,dp是最少删边的个数,然而状态转移方程不太好想。

而题解其实差不多,只不过j为剩余点的个数

这样我们就有最初状态,dp[i][1] = 子节点个数,而dp[i][j]就可以从子节点的状态推出来

dp[i][j] = min(dp[i][j],dp[i][k]-1+dp[s][j-k])  (1<=i<=n,2<=j<=sum(节点总和),1<=k<j,s为i子节点)(i中已有k个节点并从s中选择j-k个,算最少删除边数,s选上所以i->s的边不需删除,所以-1)

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>
#include <cctype>
#include <vector>
#include <iterator>
#include <set>
#include <map>
#include <sstream>
using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define pf printf
#define sf scanf
#define spf sprintf
#define pb push_back
#define debug printf("!\n")
#define MAXN 1010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#define LL long long
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pqueue priority_queue
#define INF 0x3f3f3f3f

int n,m;

struct node
{
    int y,next;
}tree[1050];

int head[505],dp[505][505],ptr=1;

int sum[505],vl[505],vis[505];

void add(int x,int y)
{
    tree[ptr].y = y;
    tree[ptr].next = head[x];
    head[x] = ptr++;
}

void dfs(int root)
{
    if(vis[root]) return;
    int i,j,k;
    vis[root] = sum[root] = 1;
    int tot = 0;

    for(i=head[root]; i!=-1; i=tree[i].next)
    {
        int p = tree[i].y;

        if(!vis[p])
        {
            dfs(p);
            sum[root]+=sum[p];
            tot++;
            //pf("i%d p%d tot%d sum%d\n",root,p,tot,sum[root]);
        }
    }

    dp[root][1] = tot;

    for(i=head[root]; i!=-1; i=tree[i].next)
    {
        int p = tree[i].y;

        for(j = sum[root];j>1;j--)
        {
            for(k = 1;k<j;k++)
            {
                if(dp[root][k]!=INF && dp[p][j-k]!=INF)
                {
                    dp[root][j] = min(dp[root][j],dp[root][k]+dp[p][j-k]-1);
                    //pf("i%d j%d k%d p%d dp%d\n",root,j,k,p,dp[root][j]);
                }
            }
        }
    }
}

int main()
{
    int i,j,k,a,b;
    while(~sf("%d%d",&n,&m) && m+n>0)
    {
        mem(head,-1);
        ptr = 1;

        for(i=1;i<=n-1;i++)
        {
            sf("%d%d",&a,&b);
            add(a,b);
            add(b,a);
        }

        mem(dp,INF);
        mem(vis,0);
        dfs(1);
        int ans = INF;
        for (i = 1; i <= n; ++i) {

            if (i == 1)
                ans = min(ans,dp[i][m]);
            else ans = min(ans,dp[i][m]+1);//非根节点要删除连到父亲节点的那条边
        }
        printf("%d\n",ans);
    }
    return 0;
}
时间: 2024-10-15 08:18:42

poj 1947 树形背包 (删边)的相关文章

poj 1947 树形背包

重做这道题 http://blog.csdn.net/woshi250hua/article/details/7632785 http://blog.csdn.net/shuangde800/article/details/10150305 http://blog.csdn.net/alps233/article/details/51190997 #include <iostream> #include <string> #include <cstring> #incl

POJ 1947 树形DP入门题

给出N个点,N-1个关系,建出树形图,问最少减去几个边能得到节点数为P的树.典型树形DP题 dp[cur][j] :记录cur结点,要得到一棵j个节点的子树去掉的最少边数 转移方程用的背包的思想 对当前树的每一个子树进行计算 砍掉此子树:   dp[cur][j]=dp[cur][j]+1; 不砍掉:           for (l=0;l<=j;l++)  dp[cur][j]=Min(dp[cur][j],dp[cur][l]+dp[next][j-l]); 枚举从该树中留l个节点其他由新

poj 1947(树形dp)

题意:一棵树上问你最少切掉几条边使得能分割出一个结点数正好为k的子树. 思路:dp[i][j]表示以i为根切掉j个结点最少要几条边. dp[v][j] = min(dp[v][j], dp[v][j-k] + dp[x][k]); 代码如下: 1 dp[v][j] = min(dp[v][j], dp[v][j-k] + dp[x][k]); 2 } 3 } 4 } 5 } 6 } 7 return vex[v]; 8 } 9 10 int main() 11 { 12 // freopen("

POJ 1155 树形背包

题意:从一个发射站发射电视,只有叶子节点是用户,收到一部分费用,所有的边都有花费,求在不亏本的情况下,最多可以让多少用户(叶子结点)收看到电视. 分析:树形背包. 状态定义: dp(i,j) : 以 i 为根的,让 j 个用户看到电视,最大获益(可以为负数).那么sz不再是原来的定义了. 最后遍历 j,第一个不为负数的就是答案. 状态转移:树形背包,dp(i,j) = max(d(i,j) , dp(i)(k)+dp(son,j-k)-w); #include <algorithm> #inc

poj 1947(树形DP+01背包)

Rebuilding Roads Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 10663   Accepted: 4891 Description The cows have reconstructed Farmer John's farm, with its N barns (1 <= N <= 150, number 1..N) after the terrible earthquake last May. Th

UVa 1407 树形背包 Caves

这道题可以和POJ 2486 树形背包DP Apple Tree比较着来做. 参考题解 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 using namespace std; 7 8 const int maxn = 500 + 10; 9 10 int n, Q; 11 vec

POJ 1155 (树形DP+背包)

题目链接: http://poj.org/problem?id=1155 题目大意:电视台转播节目.对于每个根,其子结点可能是用户,也可能是中转站.如果是中转站则花钱,如果是用户,则收钱.问在不亏本的前提下最多能有多少个用户看到节目. 解题思路: 树形背包.cost=1. 且有个虚根0,取这个虚根也要cost,所以最后的结果是dp[0][m+1]. 本题是cost=1的特殊背包问题,在两个for循环上有一个优化. for(f+1...j....cost) for(1....k...j-cost)

树形DP [POJ 1947] Rebuilding Roads

Rebuilding Roads Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 9249   Accepted: 4198 Description The cows have reconstructed Farmer John's farm, with its N barns (1 <= N <= 150, number 1..N) after the terrible earthquake last May. The

NYOJ 674 善良的国王(树形背包DP)

善良的国王 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 传说中有一个善良的国王Good,他为了不劳民伤财,每当建造一个城镇的时候都只用一条路去连接,这样就可以省很多的人力和物力,也就说如果有n个城镇,那么只需要n-1条路就可以把所有的城镇链接起来了(也就是一颗树了).但是不幸的事情发生了:有个一强大的帝国想要占领这个国家,但是由于国王Good的兵力不足,只能守护m个城镇,所以经过商量,国王Good只能从他的所有城镇中选择m个相链接的城市,并且把所有可以链接到这m