【樹形dp】poj 1947 Rebuilding Roads

CN_swords發表於2017-08-07

Link:http://poj.org/problem?id=1947

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

/*
Anniversary party POJ - 2342
題意:有n個點組成一棵樹,問至少要刪除多少條邊才能獲得一棵有p個結點的子樹?
題解:dp[i][j] 代表以i為根節點,有j個節點的最小減邊數
dp[i][1] = 與子節點的邊數+1(與父節點邊);
for(int k = 1; k < j; k++) 列舉子節點貢獻的點個數
dp[i][j] = min(dp[i][j],dp[child[i]][k]+dp[i][j-k]-2) 加上這條於子節點相連的邊
(要減2,因為算dp[child][j]這條邊算了一次,dp[root][1]這條邊也算了一次)

最後答案dp[root][j]有點不同,他與父節點相連的一條邊減去的,實際上不需要減(因為他沒父節點)
*/

const int N = 155;
const int INF = 0x3f3f3f3f;
vector<int> tree[N];
int n,p;
bool isroot[N];
int dp[N][N];
void dfs(int root)
{
    int len = tree[root].size();
    dp[root][1] = len+1;
    for(int i = 2; i <= p; i++)
        dp[root][i] = INF;
    for(int i = 0; i < len; i++)
    {
        int u = tree[root][i];
        dfs(u);
        for(int j = p; j > 1; j--)      //01揹包的思路
        {
            for(int k = 1; k < j; k++)
                dp[root][j] = min(dp[root][j],dp[u][k]+dp[root][j-k]-2);
        }
    }
}
int main(){
    scanf("%d%d",&n,&p);
    int u,v;
    for(int i = 1; i < n; i++)
    {
        scanf("%d%d",&u,&v);
        tree[u].push_back(v);
        isroot[v] = 1;
    }
    int root = 1;
    while(isroot[root])
        root++;
    dfs(root);
    dp[root][p]--;
    int mi = INF;
    for(int i = 1; i <= n; i++)\
        mi = min(mi,dp[i][p]);
    printf("%d\n",mi);
    return 0;
}


相關文章