POJ1947 - Rebuilding Roads - 樹形dp

Rebuilding Roads

題目鏈接

分類dfs and similar trees dp

1.題意概述

  • 給你一顆有n 個節點構成是樹,現在要你刪去最少的邊,使得剩下連通的點構成的樹剛好有p 個節點

2.解題思路

  • 容易想到是一個樹形dp,dp[i][j] 表示以i 節點爲根節點,得到j 個節點的子樹需要去掉的最少邊數,那麼轉移方程爲:考慮其兒子k 選或者不選——最後取個最小值就行
    • 如果不去掉k 的子樹: dp[s][i]=min(dp[s][j]+dp[k][ij]),0ji
    • 如果去掉k 的子樹:dp[s][i]=dp[s][i]+1

3.AC代碼

#define N 152
int f[N][N], sonA[N], sonB[N];
bool pa[N];
int n, p;
void init() {
    memset(pa, 0, sizeof pa);
    memset(sonA, 0, sizeof sonA);
    memset(sonB, 0, sizeof sonB);
}
void dfs(int u) {
    fill(f[u], f[u] + p + 1, INF);
    f[u][1] = 0;
    int k = sonA[u];
    while (k) {
        dfs(k);
        per(i, 1, p + 1) {
            int tmp = f[u][i] + 1;
            rep(j, 1, i)
                tmp = min(tmp, f[k][i - j] + f[u][j]);
            f[u][i] = tmp;
        }
        k = sonB[k];
    }
//  rep(i, 1, p + 1) printf("%d ", f[u][i]);
//  puts("");
}
int main() {
    while (~scanf("%d%d", &n, &p)) {
        init();
        rep(i, 1, n) {
            int u, v;
            scanf("%d%d", &u, &v);
            pa[v] = 1;
            sonB[v] = sonA[u];
            sonA[u] = v;
        }
        int sta = 0;
        rep(i, 1, n + 1)
        if (!pa[i]) {
            sta = i;
            break;
        }
        dfs(sta);
        int ans = f[sta][p];
        rep(i, 1, n + 1) {
            if (i != sta)
                ans = min(ans, f[i][p] + 1);
        }
        printf("%d\n", ans);
    }
    return 0;
}
發佈了350 篇原創文章 · 獲贊 47 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章