APIO2014 Beads ans wires[树形DP]

本文介绍了解决APIO2014 Beadsandwires问题的一种树形动态规划方法,通过定义状态G[i][x][y]来表示节点i与其子节点之间的连线状态,并使用F[i][j]来记录最大值,最终实现O(n)的时间复杂度。
[APIO2014] Beads and wires

Question:

有一个点,可以通过两种方式添加节点
Append(w,v) A p p e n d ( w , v ) :一个新的珠子 w w 和一个已经添加的珠子 v 用红线连接起来。

Insert(w,u,v) I n s e r t ( w , u , v ) :一个新的珠子 w w 插入到用红线连起来的两个珠子u,v之间。具体过程是删去 u,v u , v 之间红线,分别用蓝线连接 u,w u , w w,v w , v

现在告诉你最终状态(点和连接方式,不告诉颜色)求最大可能得分


Solution

树形DP ??
对于有贡献的链上的点貌似都会长成这个样子
Apio2014_Graph_1

而且这些点都是有顺序的 (暂且把图中 2结点 称为 A点 1,3节点称为 B点)

因为有些情况是不会出现的(好像只有一种情况),像这样
Apio2014_Graph_2
两个A点被红边或蓝边连接起来了
所以枚举状态时记录一下出现了几个A点就行

G[i][x][y] G [ i ] [ x ] [ y ] 表示 i i 点与它的子节点连了x个蓝边,且在儿子中出现了y个A点
F[i][j] 是用来记录最大值,减少代码量的

然后瞎转移就好了, O(n) O ( n )
本蒟蒻写的树形DP都是这个鬼模样,很麻烦
有点像 摧毁树状图 那个题


冗长代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
#include <cmath>
#define For(i,s,e) for(int i=(s); i<=(e); i++)
#define Rep(i,s,e) for(int i=(s); i>=(e); i--)
using namespace std;

typedef long long LL;
const int N=1000000+1, M=100000+1, Inf=1000000007;

struct Node{
    int x, nxt, w;
}T[N];

int n, m;

int h[N], len;
void addEdge(int x, int y, int w){
    T[++len]=(Node){ y, h[x], w}; h[x]=len;
}

int F[N][3], g[N][3][2];

void dp(int x, int fa){

    F[x][0]=0; F[x][1]=F[x][2]=-Inf;
    for(int p=h[x]; p; p=T[p].nxt){
        int v=T[p].x; if(v==fa) continue;

        dp(v, x);
    }

    g[x][0][0]=0, g[x][1][1]=g[x][1][0]=g[x][2][0]=g[x][2][1]=-Inf;
    for(int p=h[x]; p; p=T[p].nxt){
        int v=T[p].x; if(v==fa) continue;
        g[x][2][1]=max(g[x][2][1]+max(g[v][1][0]+T[p].w, g[v][0][0]), max(g[x][1][0]+T[p].w+max(F[v][2], g[v][0][1]), g[x][1][1]+T[p].w+g[v][0][0]));
        g[x][2][0]=max(g[x][2][0]+max(g[v][1][0]+T[p].w, g[v][0][0]), g[x][1][0]+T[p].w+g[v][0][0]);

        g[x][1][1]=max(g[x][1][1]+max(g[v][1][0]+T[p].w, g[v][0][0]), g[x][0][0]+T[p].w+max(F[v][2], g[v][0][1]));
        g[x][1][0]=max(g[x][1][0]+max(g[v][1][0]+T[p].w, g[v][0][0]), g[x][0][0]+T[p].w+g[v][0][0]);

        g[x][0][1]=max(g[x][0][1]+max(g[v][1][0]+T[p].w, g[v][0][0]), g[x][0][0]+max(T[p].w+g[v][1][1], max(F[v][2], g[v][0][1])));
        g[x][0][0]=g[x][0][0]+max(g[v][1][0]+T[p].w, g[v][0][0]);
    }

    F[x][0]=max(g[x][0][0], g[x][0][1]);
    F[x][1]=max(g[x][1][0], g[x][1][1]);
    F[x][2]=max(g[x][2][0], g[x][2][1]);
}

int main(){
    ios::sync_with_stdio(false);
    cin>>n;

    For(i,1,n-1){
        int a, b, c; cin>>a>>b>>c;
        addEdge(a, b, c); addEdge(b, a, c);
    }

    dp(1,0);

    cout<<max(F[1][0], F[1][2])<<endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值