【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 }

 

posted @ 2013-01-09 01:46  lzqxh  阅读(332)  评论(0编辑  收藏  举报