1. 程式人生 > >[NOI2002] 貪吃的九頭龍

[NOI2002] 貪吃的九頭龍

二叉樹 mes 完整 tro 相同 出了 while clas 樹形dp

題目類型:樹形DP

傳送門:>Here<

題意:有一只九頭龍要吃了一顆樹,給出一棵\(N\)個節點的帶邊權的樹。九頭龍有\(M\)個頭,其中一個是大頭,大頭要吃恰好\(K\)個節點,其他頭吃幾個隨意。如果一個頭吃了一個連通塊,那麽他們會把樹枝也吃下去,獲得邊權那麽多的難受值。先要吃完整棵樹,使難受值總和最小

解題思路

首先會發現題目蘊含著一個奇妙的性質。會得到一條邊的難受值當且僅當一條邊相鄰的兩個節點是被同一個頭吃的,換句話說如果相鄰的兩個節點是不同的龍吃的那麽就不會獲得該邊的難受值。當我們確定大頭吃的\(K\)個點之後,由於剩下的頭每個頭想吃幾個就吃幾個,由於給出了一顆樹,一定存在一種方案使得任意兩個相鄰的點是不同的頭。事實上,只需要多余的兩種頭就可以了。換句話說,我們可以把所有多余\(3\)

個頭的情況看做是\(3\)個頭的。

當然需要特殊考慮\(M=2\)的情況,此時剩余的只有一個頭了,需要特殊處理

於是,難受值完全取決於大頭的那\(K\)個節點如何選擇。

考慮樹形DP:\(f[u][j][0/1]\)表示子樹\(u\)中大頭吃\(j\)個的最小難受值之和,其中\(k=0\)表示大頭不吃根節點,反之亦然

於是可以發現我們只需要判斷\(u,v\)的顏色是否相同即可以轉移:當\(M>2\)時,除非\(u,v\)都是\(1\),否則不考慮。當\(M=2\)時,\(0,1\)可以直接表示他們的頭了,所以也就是\(u^v\)

於是我們可以初步得到轉移方程:\[f[u][j][1]=Min\{f[v][k][0]+g[u][j-k][1],f[v][k][1]+g[u][j-k][1]+cost(u,v)\}\]

\[f[u][j][0]=Min\{f[v][k][0]+g[u][j-k][0]+cost(u,v)*[M=2],f[v][k][1]+g[u][j-k][0]\}\]

註意方程中的\(g\)數組,我們好像並沒有涉及到它。事實上我們發現,方程的轉移時需要枚舉\(k\)作為中介的,但是眾多兒子,每個兒子的\(k\)如何分配?題目給出了一顆多叉樹,使得這一步非常復雜。

聯想二叉樹的做法,二叉樹時當確定一個兒子是\(k\)時,馬上就能確定另一個兒子是\(j-k\)。如果也用這種方式來處理多叉樹就好了。於是我們在考慮第\(j\)個兒子的時候,可以記錄前\(j-1\)個已經做過的兒子的最優值。而上一輪留下的結果正是\(f\)

數組!但由於\(f\)數組在不斷的更新,肯定不能直接拿過來用,因此需要一個臨時數組\(g\)來記錄上一輪的結果,其實形象的理解,就是把前面的\(j-1\)個兒子並在了一起

由於每一輪\(f\)數組都要重新更新,因此每一輪都要重新賦值\(+∞\)。另外很容易得到另個初始化條件:\(f[u][0][0]=1, f[u][1][1]=0\)。意義都很顯然

Code

/*By DennyQi 2018.8.20*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define  r  read()
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
using namespace std;
typedef long long ll;
const int MAXN = 310;
const int MAXM = 610;
const int INF = 1061109567;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
    if(c == '-') w = -1, c = getchar();
    while(c >= '0' && c <= '9') x = (x<<3) + (x<<1) + c - '0', c = getchar();return x * w;
}
int N,M,K,x,y,z;
int first[MAXM],nxt[MAXM],to[MAXM],cost[MAXM],cnt;
int f[MAXN][MAXN][2], g[MAXN][2];
inline void add(int u, int v, int w){
    to[++cnt]=v, cost[cnt]=w, nxt[cnt]=first[u], first[u]=cnt;
}

int main(){
    N = r, M = r, K = r;
    if(M-1 + K > N){
        printf("-1");
        return 0;
    }
    for(int i = 1; i < N; ++i){
        x = r, y = r; z = r;
        add(x, y, z);
        add(y, x, z);
    }
    memset(f, 0x3f, sizeof(f));
    DP(1, 0);
    printf("%d", f[1][K][1]);
    return 0;
}

[NOI2002] 貪吃的九頭龍