0818 T3 cross

来源:互联网 发布:两心之外无人知意思 编辑:程序博客网 时间:2024/05/19 17:25

原题链接

20 pts

枚举公共点,求出它的每个出度方向上到其他点的最长路径长度.
取前4长加起来.

50 pts

其实暴力是能过 50% 的数据的.
只要在枚举公共点时先判断一下它是否至少连接了4个其他点即可.
这是为了告诉大家
就算写暴力也要把必要的优化加上

100 pts

树形动态规划
任选一个点作为树根建立一棵树
f[i][0…3]分别表示从i开始,向下走到某个叶子节点为止最长, 次长, 第三长, 第四长的路径长度.
按从叶子向树根的顺序进行DP, 每次用f[i][0]去尝试更新i的父亲, 可以很方便地求出f数组.
用g[i]表示以i为起点, 第一步向i的父亲方向走的最长路径长度.
第一步从i走到i的父亲fa[i]后, 第二步有两种选择:
1 . 继续往父亲走,则最长长度为g[fa[i]]。
2 . 向下走。为了是路径最长,首选当然是f[fa[i]][0], 但是如果i处在f[fa[i]][0]对应的那条路径上,那就只能选择第二长的f[fa[i]][1]了。
有了f数组和g数组后, 枚举公共点i,那么答案就是:
max{f[i,0]+f[i,1]+f[i,2]+f[i,3] , g[i]+f[i,0]+f[i,1]+f[i,2]}。

代码

#include<cstdio>#include<algorithm>using namespace std;const int maxn = 100000 + 11;const int inf = 20000011;int a[maxn*3], nx[maxn*3], s[maxn*3], tl[maxn], g[maxn],    f[maxn][4], fs[maxn], fa[maxn], c[maxn], n, ln, ans; int main() {    scanf("%d", &n);    for(int i = 1; i <= n; ++i) {        tl[i] = ++ln;//以每个点作为开头         for(int j = 0; j < 4; ++j)             f[i][j] = -inf;//储存四个长度     }    int u, v, w;    for(int i = 2; i <= n; ++i) {//加边         scanf("%d%d%d", &u, &v, &w);        tl[u] = nx[tl[u]] = ++ln;        a[ln] = v;//边ln的的结尾         tl[v] = nx[tl[v]] = ++ln;        a[ln] = u;        s[ln-1] = s[ln] = w;//两条边的权值     }    c[1] = 1;//队列c辅助建树     int l = 0, r = 1;//左闭右开     fa[1] = -1;//以1号点为根,建树     while(l < r) {        int k = c[++l];        for(int i = nx[k]; i; i = nx[i])            if(!fa[a[i]]) {                r++;                c[r] = a[i];                fa[c[r]] = k;                fs[a[i]] = s[i];//点a[i]的指向父亲节点的边的权值fs[a[i]]             }    }    for(int i = r; i > 1; --i) {//按建树顺序反向求f数组         int k = c[i];        w = (f[k][0] < 0) ? 0 : f[k][0];//w:向下走的最长距离         for(int j = 0; j <= 3; ++j) {//处理k的父亲节点             if(w+fs[k] >= f[fa[k]][j]) {//如果大于前4中的一个                 for(int b = 3; b > j; --b) {//其他向后退一位                     f[fa[k]][b] = f[fa[k]][b-1];                }                f[fa[k]][j] = w + fs[k];//更新大于的那个                 break;//更新后及时退出             }        }    }     g[1] = -inf;//1号点(根节点)无法向上走     for(int i = 1; i <= n; ++i) {//从上到下遍历所有点         int k = c[i];        ans = max(f[k][0]+f[k][1]+f[k][2]+f[k][3], ans);//只向下走         ans = max(g[k]+f[k][0]+f[k][1]+f[k][2], ans);//向上走        for(int i = nx[k]; i; i = nx[i]) {//遍历k的所有边             if(a[i] != fa[k]) {//该点不是k的父亲节点, 即遍历k的所有儿子结点                 g[a[i]] = max(g[k]+s[i], g[a[i]]);                if(s[i]+f[a[i]][0] == f[k][0]) {//处理儿子结点在当前节点的最长路径上的情况                     w = f[k][1];                } else {                    w = f[k][0];                }//确保w是k的向下的边中除了当前儿子结点所在边之外最长的路径                 if(w < 0) w = 0;                g[a[i]] = max(w+s[i], g[a[i]]);            }        }    }    if(ans <= 0) ans = -1;    printf("%d\n", ans);    return 0;}
原创粉丝点击