1. 程式人生 > >藍橋杯 演算法訓練-結點選擇

藍橋杯 演算法訓練-結點選擇

問題描述

有一棵 n 個節點的樹,樹上每個節點都有一個正整數權值。如果一個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?

解題思路

很簡單的一道入門樹狀dp,把求最終解分成兩種情況

  1. 根結點被選中,則所有的子節點一定不能被選中
  2. 根節點沒被選中,則子節點可被選中,注意是可以被選中,有的最優解中很可能不選擇某一個子節點而選擇子節點的子節點。

d(r,s) 為r頂點在選中或為選中時的該樹的最大權值和,則可列出狀態轉移方程

d(r,1)=v(r)+d(ci,0)
d(r,0)=max{d(ci,1),d(ci,0)}

則最終的結果就是選根節點兩種情況中最大的一個

#include <iostream>
#include <cstring>
#define N 100000

#define MAX(x, y) ((x)>(y)?(x):(y))

struct edge
{
    int to;
    int next;
};
int tree[N];

int weight[N];

edge edges[2*N];

int d[N][2];

int len = 0;

void add(int x, int y)
{
    edges[len].to = y;
    edges[len].next = tree[x];
    tree[x] = len++;
}

void
dp(int root, int p) { if(tree[root] == -1) { d[root][0] = 0; d[root][1] = weight[root]; return; } for(int i = tree[root]; i != -1; i = edges[i].next) { int child = edges[i].to; if(child == p) continue; if(!d[child][1]) dp(child, root); d[root][0
] += MAX(d[child][1], d[child][0]); d[root][1] += d[child][0]; } d[root][1] += weight[root]; } int main() { memset(tree, -1, sizeof(tree)); int n; std::cin >> n; for(int i = 1; i <= n; i++) std::cin >> weight[i]; int x, y; for(int i = 0; i < n-1; i++) { std::cin >> x >> y; add(x, y); add(y, x); } dp(1, -1); int max = MAX(d[1][0], d[1][1]); std::cout << max << std::endl; }