POJ 1192 最优连通子集(树上DP)

4 篇文章 0 订阅

题目链接:http://poj.org/problem?id=1192


题意:给出一个由平面整点构成的树(两点相邻当且仅到曼哈顿距离为1),有N个点,每个点有权值。求这棵树的权值最大的子树(只求这个子树的权值)

思路:用树上DP解决。称所要求的权值最大子树为“最大集”

首先要把这个树看成一个有根树。对于每个节点,维护两个数,分别是该子树中最大集的权值,和包含当前这个节点的最大集的权值。对于根来说,前一个数就是要求的答案。而后一个数是为了合并一个节点的各个子树中的答案。如果在某个子树中最大集不包含根节点则在上一层中无法将这个最大集合并。


状态转移:只需注意到一棵树的最大集或者是一个子树的最大集(对应维护的第一个数),或者是包含根节点的最大集(对应第二个)。而包含根节点的最大集,除去根节点之外还包含它所有子树的包含(对应子树的)根节点且权值为正的最大集(如果权值为负,则总贡献是负的)。


复杂度:因为每个点的邻居最多4个(平面整点),复杂度是O(N)。


实现:使用DFS。在遍历所有邻居时要去除上一层的点,使用vis[]来标记。


代码如下:

/*
    PROG: POJ1192
    PROB:
*/

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;

#define DEBUG 1
#define LOG(...) do { if (DEBUG) fprintf(stderr, __VA_ARGS__); } while(0)

#define MAXN 1005
int M[MAXN][MAXN];
char vis[MAXN][MAXN];
int N, val[MAXN];

struct Point {
    int x, y;
    Point(int x=0, int y=0) : x(x), y(y) {}
};

typedef vector<Point> VP;
typedef pair<int, int> PII;

const int dx[] = {1, -1, 0, 0};
const int dy[] = {0, 0, 1, -1};


PII wk(int x, int y) {
    PII ret(-10000000, -10000000);
    vis[x][y] = 1;
    int curr = val[M[x][y]];
    for (int dir = 0; dir < 4; ++dir) {
        int tx = x+dx[dir], ty = y+dy[dir];
        if (tx<0||ty<0||tx>=MAXN||ty>=MAXN) continue;
        if (vis[tx][ty]||M[tx][ty]<0) continue;
        PII tmp(wk(tx,ty));
        ret.first = max(ret.first, tmp.first);
        if (tmp.second>0) curr += tmp.second;
    }
    ret.second = curr;
    ret.first = max(ret.first, ret.second);
    vis[x][y] = 0;
    return ret;
}

int main(void) {
    scanf("%d", &N);
    VP pts(N);
    int x, y, mx, my; scanf("%d%d%d", &mx, &my, val);
    pts[0] = Point(mx, my);
    for (int i = 1; i < N; ++i) {
        scanf("%d%d%d", &x, &y, val+i);
        pts[i] = Point(x, y);
        mx = min(mx, x);
        my = min(my, y);
    }
    memset(M, -1, sizeof(M));
    // LOG("%d, %d\n", mx, my);
    for (int i = 0; i < N; ++i) {
        pts[i].x -= mx, pts[i].y -= my;
        M[pts[i].x][pts[i].y] = i;
        // LOG("%d %d\n", pts[i].x, pts[i].y);
    }
    PII ans = wk(pts[0].x, pts[0].y);
    printf("%d\n", ans.first);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值