1. 程式人生 > >HAOI2015 樹上染色

HAOI2015 樹上染色

傳送門

非常神奇的樹形DP……

可能和大多數人一樣吧,一開始我令dp[i][j]表示以i為根的子樹選擇j個黑點的最大價值,之後就不會轉移了……

後來發現這個狀態設的並不對,因為我們要考慮的是對答案的總貢獻,而不是每個子樹內的價值,也就是我們要考慮的是每條邊對答案的貢獻。

這個式子很顯然的,但是像我這樣是肯定考慮不到的……

於是就把這個問題轉化成了樹上揹包,我們把在子樹內選取的黑點看作體積,貢獻看作價值,之後我們進行DP即可。注意這道題的關鍵是,對於答案的貢獻並不只侷限於子樹內,而是對於整棵樹而言。

令dp[i][j]表示以i為根的子樹選擇j個黑點的最大貢獻,那麼dp[i][j] = max(dp[i][j],dp[i][t] + dp[v][j-t] + val),val是計算的貢獻。

這樣進行DP就可以啦。

看一下程式碼。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#include<queue>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')

using
namespace std; typedef long long ll; const int M = 2005; const int INF = 1000000009; ll read() { ll ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans
+= ch - '0'; ch = getchar(); } return ans * op; } struct edge { int next,to; ll v; }e[M<<1]; ll dp[M][M],ans,x,y,z,ecnt,head[M]; int size[M],n,k; void add(int x,int y,int z) { e[++ecnt].to = y; e[ecnt].next = head[x]; e[ecnt].v = z; head[x] = ecnt; } void dfs(int x,int fa) { size[x] = 1,dp[x][0] = dp[x][1] = 0; for(int i = head[x];i;i = e[i].next) { if(e[i].to == fa) continue; dfs(e[i].to,x); size[x] += size[e[i].to]; } for(int i = head[x];i;i = e[i].next) { int v = e[i].to; if(v == fa) continue; per(j,min(k,size[x]),0) rep(p,0,min(j,size[v])) { if(dp[x][j-p] != -1) { ll val = (ll)(e[i].v * p * (k-p)) + (ll)(e[i].v * (size[v] - p) * (n-k+p-size[v])); dp[x][j] = max(dp[x][j],dp[v][p] + dp[x][j-p] + val); } } } } int main() { n = read(),k = read(); memset(dp,-1,sizeof(dp)); rep(i,1,n-1) x = read(),y = read(),z = read(),add(x,y,z),add(y,x,z); dfs(1,0); printf("%lld\n",dp[1][k]); return 0; }