首页 > HDU5886 Tower Defence 【两遍树形dp】【最长链预处理】

HDU5886 Tower Defence 【两遍树形dp】【最长链预处理】

题意:N个点的一棵带权树。切掉某条边的价值为切后两树直径中的最大值。求各个边切掉后的价值和(共N-1项)。

解法一:

强行两遍dp,思路繁琐,维护东西较多:

dis表示以i为根的子树的直径,dis2表示切掉以i为根的子树后的直径。

第一遍dp,记录

  down[][0]:从i结点向下的最大距离

  down[][1]:与down[][0]无交集的向下次大距离

  dis:以i为根的子树的直径

第二遍dp,记录

  up:从i结点向上的最远距离, 可以是w+父节点的up,也可以是w+父节点的down(判断一下down是否与w有重合)

  dis2:切掉以i为根的子树后的直径

  1 #include 
  2 using namespace std;
  3 #define X first
  4 #define Y second
  5 #define pii pair
  6 #define mp make_pair
  7 typedef long long ll;
  8 const int N = 1e5+5;
  9 void gmax(int& a, int b){ if(a < b) a = b;}
 10 int n;
 11 ll ans;
 12 int down[N][2], up[N], dis[N], dis2[N];
 13 
 14 int head[N], nex[N*2], tot;
 15 pii edge[N*2];
 16 void init(){
 17     tot = 0;
 18     memset(head, -1, sizeof(head));
 19     memset(down, 0, sizeof(down));
 20     memset(up, 0, sizeof(up));
 21     memset(dis, 0, sizeof(dis));
 22     memset(dis2, 0, sizeof(dis2));
 23 }
 24 void add(int u, int v, int w){
 25     edge[tot] = mp(v, w);
 26     nex[tot] = head[u];
 27     head[u] = tot++;
 28 }
 29 //down[][0]:从i结点向下的最大距离
 30 //down[][1]:与down[][0]无交集的向下次大距离
 31 //dis:以i为根的子树的直径
 32 void dfs(int fa, int x){
 33 //    printf("dfs x %d, fa %d
", x, fa);
 34 //down[x][0] = down[x][1] = dis[x] = dis2[x] = up[x] = 0;
 35     for(int i = head[x]; ~i; i = nex[i]){
 36         int y = edge[i].X, w = edge[i].Y;
 37         if(y == fa) continue ;
 38         dfs(x, y);
 39         if(down[y][0]+w > down[x][0])
 40             down[x][1] = down[x][0], down[x][0] = down[y][0]+w;
 41         else if(down[y][0]+w > down[x][1])
 42             down[x][1] = down[y][0]+w;
 43         gmax(dis[x], dis[y]);
 44     }
 45     gmax(dis[x], down[x][0]+down[x][1]);
 46 }
 47 //up:从i结点向上的最远距离
 48 //dis2:切掉以i为根的子树后的直径
 49 multiset<int>::iterator it;
 50 void dfs2(int fa, int x){
 51     if(fa != -1) ans += max(dis[x], dis2[x]);
 52 //    printf("fa %d, x %d
", fa, x);
 53     multiset<int> se, se2;//兄弟树的直径, x往各个兄弟树的最长路
 54     for(int i = head[x]; ~i; i = nex[i]){
 55         int y = edge[i].X, w = edge[i].Y;
 56         if(y == fa) continue ;
 57         se.insert( dis[y] );
 58         se2.insert( down[y][0]+w );
 59     }
 60     for(int i = head[x]; ~i; i = nex[i]){
 61         int y = edge[i].X, w = edge[i].Y;
 62         if(y == fa) continue ;
 63         up[y] = up[x]+w;
 64         if(down[y][0]+w == down[x][0])
 65             gmax(up[y], down[x][1]+w);
 66         else gmax(up[y], down[x][0]+w);
 67         it = se.find( dis[y] ), se.erase(it);
 68         it = se2.find( down[y][0]+w ), se2.erase(it);
 69         dis2[y] = dis2[x];
 70         if(!se.empty())
 71             gmax(dis2[y], *se.rbegin());
 72         if(se2.empty()) gmax(dis2[y], up[x]);
 73         else gmax(dis2[y], up[x]+*se2.rbegin());
 74         if(se2.size() >= 2){
 75             int tmp = 0;
 76             it = se2.end();
 77             tmp += *--it;
 78             tmp += *--it;
 79             gmax(dis2[y], tmp);
 80         }
 81         dfs2(x, y);
 82         se.insert( dis[y] );
 83         se2.insert( down[y][0]+w );
 84     }
 85 }
 86 void debug(int n){
 87     for(int i = 1; i <= n; i++)
 88         printf("%d: down0 %d, down1 %d, dis %d, dis2 %d, up %d
", i, down[i][0], down[i][1], dis[i], dis2[i], up[i]);
 89 }
 90 int main(){
 91     int t, u, v, w; scanf("%d", &t);
 92     while(t--) {
 93         init();
 94         scanf("%d", &n);
 95         for(int i = 1; i < n; i++){
 96             scanf("%d%d%d", &u, &v, &w);
 97             add(u, v, w), add(v, u, w);
 98         }
 99         ans = 0;
100         dfs(-1, 1);
101         dfs2(-1, 1);
102         printf("%lld
", ans);
103     }
104     return 0;
105 }
View Code

 

解法二:

先求出原树的直径。

从直径两端分别来一次dp

切的边不在直径上,价值为直径;

切的边在直径上,由直径两端的dp得解。

 

转载于:https://www.cnblogs.com/dirge/p/5975794.html

更多相关:

  •         Apache POI是一个开源的利用Java读写Excel,WORD等微软OLE2组件文档的项目。        我的需求是对Excel的数据进行导入或将数据以Excel的形式导出。先上简单的测试代码:package com.xing.studyTest.poi;import java.io.FileInputSt...

  • 要取得[a,b)的随机整数,使用(rand() % (b-a))+ a; 要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a; 要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1; 通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 要取得a到b之间的...

  • 利用本征图像分解(Intrinsic Image Decomposition)算法,将图像分解为shading(illumination) image 和 reflectance(albedo) image,计算图像的reflectance image。 Reflectance Image 是指在变化的光照条件下能够维持不变的图像部分...

  • 题目:面试题39. 数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2 限制: 1 <= 数组长度 <= 50000 解题: cl...

  • 题目:二叉搜索树的后序遍历序列 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树:      5     /    2   6   /  1   3示例 1: 输入: [1,6,3,2,5] 输出...