【treedp】codeforces 70E.Information Reform
http://codeforces.com/problemset/problem/70/E
给一棵树,让你在树上建立若干个区域中心(每建立一个需花费一定代价),最终每个节点到最近区域中心距离为Li, 则会产生d[Li]的代价,求一个方案使得最终总代价最小。
神级树形dp,看完题解才会的,感觉是我之前完全没有见识过的一类树形dp思路,太孤陋寡闻了!
如图,dp[T,g]作为一个状态,T是原图中截断某条边剩下的子数,容得T数目是O(n)级别的,g是树上某个顶点(dp[T,g]表示T这棵子树的根v,以g为中心最小代价,同时树上其他顶点可以以其他结点作为中心。原题解中g限定为T上的节点,显然这样定义状态之后,我们可以枚举T之后枚举g,之后遍历一下T,dp一下某条边砍于不砍就可以得到O(n^3)方法,实现时候可以拓扑构造这个树的过程来枚举T。
但是,参考gxx的程序之后,发现一个极度潇洒的实现,dp[T,g]如果g定义不再是T这棵子树内,而是整棵树上的某个节点,之后就可以简单的在dfs的过程中用几个for实现程序,原理于上面是一样的,但是这样写会覆盖到某些不规范的解,我们但是我们可以通过正解的形式出发证明这样会覆盖到正解,同时证明显然不规范的解是肯定差于正解的,这样的思路来证明其正确性,具体证明这里略了。。
附代码(dp[T,g]中g的定义是整个树)
View Code
1 //By Lin 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define maxn 205 6 using namespace std; 7 8 int ecnt; 9 struct Edge{ 10 int to; 11 Edge *next; 12 }*mat[maxn],edges[maxn*2]; 13 void link(int x,int to){ 14 edges[ecnt].to = to; 15 edges[ecnt].next = mat[x]; 16 mat[x] = &edges[ecnt++]; 17 } 18 19 int n,K,dp[maxn][maxn],best[maxn][maxn]; 20 int d[maxn],dis[maxn][maxn],ans[maxn]; 21 22 void dfs(int x, int father, int dis[]){ 23 for ( Edge *p = mat[x];p ; p = p->next ){ 24 int to = p->to; 25 if ( father == to ) continue; 26 dis[to] = dis[x]+1; 27 dfs(to,x,dis); 28 } 29 } 30 31 void solve(int x,int father){ 32 for ( Edge *p = mat[x];p ; p = p->next ){ 33 int to = p->to; 34 if ( father == to ) continue; 35 solve(to,x); 36 } 37 for (int i = 1; i<=n; i++) { 38 dp[x][i] = d[dis[x][i]]; 39 for ( Edge *p = mat[x];p ; p = p->next ){ 40 int to = p->to; 41 dp[x][i] += min( dp[to][i] , dp[to][best[to][i]]+K ); 42 } 43 } 44 int g = 1,h = 2; 45 if ( dp[x][g] > dp[x][h] ) swap(g,h); 46 for (int i = 3; i<=n; i++) 47 if ( dp[x][i] < dp[x][g] ) h = g , g = i; 48 else if ( dp[x][i] < dp[x][h] ) h = i; 49 for (int i = 1; i<=n; i++) best[x][i] = i==g?h:g; 50 } 51 52 void get(int x,int father,int root){ 53 ans[x] = root; 54 for ( Edge *p = mat[x];p ; p = p->next ){ 55 int to = p->to; 56 if ( to == father ) continue; 57 if ( dp[to][root] < dp[to][best[to][root]]+K ) 58 get(to,x,root); 59 else 60 get(to,x,best[to][root]); 61 } 62 } 63 int main(){ 64 scanf("%d%d", &n, &K ); 65 for (int i = 1;i<n; i++) scanf("%d", &d[i] ); 66 for (int i = 1,x,y; i<n; i++){ 67 scanf("%d%d", &x, &y ); 68 link(x,y) , link(y,x); 69 } 70 for (int i = 1; i<=n; i++) dfs(i,-1,dis[i]); 71 solve(1,-1); 72 int g = 1; 73 for (int i = 1; i<=n; i++) if ( dp[1][i] < dp[1][g] ) g = i; 74 printf("%d\n" , dp[1][g]+K ); 75 get( 1 , -1 , g ); 76 for (int i = 1; i<=n; i++) printf("%d%c" , ans[i] , i ==n?'\n':' ' ); 77 return 0; 78 }