1. 程式人生 > >poj 1192 最優連通子集 樹狀dp

poj 1192 最優連通子集 樹狀dp

最優連通子集

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 2431 Accepted: 1294

Description

眾所周知,我們可以通過直角座標系把平面上的任何一個點P用一個有序數對(x, y)來唯一表示,如果x, y都是整數,我們就把點P稱為整點,否則點P稱為非整點。我們把平面上所有整點構成的集合記為W。
定義1 兩個整點P1(x1, y1), P2(x2, y2),若|x1-x2| + |y1-y2| = 1,則稱P1, P2相鄰,記作P1~P2,否則稱P1, P2不相鄰。
定義 2 設點集S是W的一個有限子集,即S = {P1, P2,..., Pn}(n >= 1),其中Pi(1 <= i <= n)屬於W,我們把S稱為整點集。
定義 3 設S是一個整點集,若點R, T屬於S,且存在一個有限的點序列Q1, Q2, ?, Qk滿足:
1. Qi屬於S(1 <= i <= k);
2. Q1 = R, Qk = T;
3. Qi~Qi + 1(1 <= i <= k-1),即Qi與Qi + 1相鄰;
4. 對於任何1 <= i < j <= k有Qi ≠ Qj;
我們則稱點R與點T在整點集S上連通,把點序列Q1, Q2,..., Qk稱為整點集S中連線點R與點T的一條道路。
定義4 若整點集V滿足:對於V中的任何兩個整點,V中有且僅有一條連線這兩點的道路,則V稱為單整點集。
定義5 對於平面上的每一個整點,我們可以賦予它一個整數,作為該點的權,於是我們把一個整點集中所有點的權的總和稱為該整點集的權和。
我們希望對於給定的一個單整點集V,求出一個V的最優連通子集B,滿足:
1. B是V的子集
2. 對於B中的任何兩個整點,在B中連通;
3. B是滿足條件(1)和(2)的所有整點集中權和最大的。

Input

第1行是一個整數N(2 <= N <= 1000),表示單整點集V中點的個數;
以下N行中,第i行(1 <= i <= N)有三個整數,Xi, Yi, Ci依次表示第i個點的橫座標,縱座標和權。同一行相鄰兩數之間用一個空格分隔。-10^6 <= Xi, Yi <= 10^6;-100 <= Ci <= 100。

Output

僅一個整數,表示所求最優連通集的權和。

Sample Input

5
0 0 -2
0 1 1
1 0 1
0 -1 1
-1 0 1

Sample Output

2

題目意思真繞,大致上是在給的點裡選擇若干個點使權值和最大,並且原給出的點構成一棵無根樹。一開始沒理解樣例,後來發現就是把所有的點都選上。
建樹: 題目意思已經說了已經是構成樹了,所以只要把相鄰的節點連線起來就好。
轉移:dp[t][0(/1)] 表示以t為根節點的子樹在不選擇(/選擇)t節點的情況下的最大權和。
     dp[t][0] = max(max(dp[ti][0], dp[ti][1])); // 子樹狀態隨意,但只能選一個,因為根節點沒有選擇無法連線多棵子樹。
     dp[t][1] = sum(dp[ti][0]>0); // 所有權和大於0的子樹均可以選入,也可能沒有選入一棵子樹。

#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

const int INF = 999999999;

std::vector<int > v[1010];

struct Node {
	int x;
	int y;	
	int c;
}a[1010];

int dp[1010][2];

void link (int _a, int _b) {
	if (abs(a[_a].x - a[_b].x) + abs(a[_a].y - a[_b].y) == 1) {
		v[_a].push_back(_b);
		v[_b].push_back(_a);
	}
}

void Tdp(int t, int fa) {
	dp[t][0] = 0;
	dp[t][1] = a[t].c;
	
	int flag = -INF;

	for (int i=0; i<v[t].size(); i++) {
		
		if (v[t][i] == fa) continue;
		
		int _t = v[t][i];
		Tdp(_t, t);

		dp[t][0] = max(dp[t][0], max(dp[_t][0], dp[_t][1]));
		if (dp[_t][1] > 0) {
			dp[t][1] += dp[_t][1];
		}
	}
}

int main () {
	int n;
	cin >> n;
	for (int i=0; i<n; i++) {
		cin >> a[i].x >> a[i].y >> a[i].c;
 	}

 	for (int i=0; i<n; i++) {
 		for (int j=i+1; j<n; j++) {
 			link(i, j);
 		}
 	}

 	Tdp(0, -1);

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

 	return 0;
}