HAOI2015 樹上染色
阿新 • • 發佈:2018-11-08
非常神奇的樹形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') usingnamespace 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; }