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; }
当你意识到,每个上一秒都成为永恒。