POJ - 3659 Cell Phone Network(树形dp---树的最小点支配集)

题意:有N个点,N-1条边,任意两点可达,由此形成了一棵树。选取一个点a,它可覆盖自己以及与自己相邻的点,选取尽量少的点a,使得树中所有点都被覆盖,即求树的最小点支配集。

分析:

1、对于每一个点cur,要想使其被覆盖,有三种情况:

dp[cur][0]---在该点建立塔

dp[cur][1]---在该点的子结点建立塔

dp[cur][2]---在该点的父结点建立塔

2、对于点cur的子结点x,要使其被覆盖:

(1)dp[cur][0] += Min(Min(dp[x][0], dp[x][1]), dp[x][2]);

在cur处建塔的情况下,x可建塔,x的子节点可建塔,x的父结点即cur建塔,三者取最小值,并累加。

(2)dp[cur][2] += Min(dp[x][0], dp[x][1]);

在cur的父结点建塔的情况下,x可建塔,x的子结点可建塔,两者取最小值,并累加。

(3)dp[cur][1] += Min(dp[x][0], dp[x][1]);

在cur的子结点建塔的情况下,至少需要cur的一个子结点建塔cur才能被覆盖,所以对于每一个子结点x,x可建塔,x的子结点可建塔,两者取最小值

与此同时,记录在取最小值的情况下,cur是否有能建塔的子结点,若没有,需要将cur的一个子结点变成可建塔,选取abs(dp[x][1] - dp[x][0])最小的子结点x改变即可。

3、dp[cur][0]最后要加1,因为在cur点要建塔。

#pragma comment(linker, "/STACK:102400000, 102400000")
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<iostream>
#include<sstream>
#include<iterator>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#define Min(a, b) ((a < b) ? a : b)
#define Max(a, b) ((a < b) ? b : a)
const double eps = 1e-8;
inline int dcmp(double a, double b){
    if(fabs(a - b) < eps) return 0;
    return a > b ? 1 : -1;
}
typedef long long LL;
typedef unsigned long long ULL;
const int INT_INF = 0x3f3f3f3f;
const int INT_M_INF = 0x7f7f7f7f;
const LL LL_INF = 0x3f3f3f3f3f3f3f3f;
const LL LL_M_INF = 0x7f7f7f7f7f7f7f7f;
const int dr[] = {0, 0, -1, 1, -1, -1, 1, 1};
const int dc[] = {-1, 1, 0, 0, -1, 1, -1, 1};
const int MOD = 1e9 + 7;
const double pi = acos(-1.0);
const int MAXN = 10000 + 10;
const int MAXT = 10000 + 10;
using namespace std;
int N;
vector<int> v[MAXN];
int dp[MAXN][5];
void dfs(int cur, int father){
    memset(dp[cur], 0, sizeof dp[cur]);
    int len = v[cur].size();
    int dif = INT_INF;
    bool ok = false;
    for(int i = 0; i < len; ++i){
        int x = v[cur][i];
        if(x == father) continue;
        if(dp[x][0] == INT_INF) dfs(x, cur);
        dp[cur][0] += Min(Min(dp[x][0], dp[x][1]), dp[x][2]);
        dp[cur][2] += Min(dp[x][0], dp[x][1]);
        dif = Min(dif, abs(dp[x][1] - dp[x][0]));
        if(dp[x][0] < dp[x][1]){
            ok = true;
            dp[cur][1] += dp[x][0];
        }
        else{
            dp[cur][1] += dp[x][1];
        }
    }
    ++dp[cur][0];
    if(!ok) dp[cur][1] += dif;
}
int main(){
    scanf("%d", &N);
    for(int i = 0; i < N - 1; ++i){
        int a, b;
        scanf("%d%d", &a, &b);
        v[a].push_back(b);
        v[b].push_back(a);
    }
    memset(dp, INT_INF, sizeof dp);
    dfs(1, -1);
    printf("%d\n", Min(dp[1][0], dp[1][1]));
    return 0;
}

  

时间: 2024-10-12 13:34:29

POJ - 3659 Cell Phone Network(树形dp---树的最小点支配集)的相关文章

POJ 3659 Cell Phone Network(树的最小支配集)(贪心)

Cell Phone Network Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6781   Accepted: 2429 Description Farmer John has decided to give each of his cows a cell phone in hopes to encourage their social interaction. This, however, requires hi

树形DP 树的最小支配集,最小点覆盖与最大独立集

最小支配集: 从V中选取尽量少的点组成一个集合,让V中剩余的点都与取出来的点有边相连. (点) 最小点覆盖: 从V中选取尽量少的点组成一个集合V1,让所有边(u,v)中要么u属于V1,要么v属于V1 (边) 最大独立集: 从V中选取尽量多的点组成一个集合,让这些点中间没有边项链,也就是说对于任何一条边,u,v不能同时属于集合V1. 1.贪心算法 首先选取一个点为根节点,求出所有节点对应的DFS序列,按照所得序列反向进行贪心,这样保证对于每个点来说,当子树都被处理过之后才会处理该节点 int p[

POJ 3659 Cell Phone Network (树dp)

题目链接:http://poj.org/problem?id=3659 给你一个树形图,一个点可以覆盖他周围连接的点,让你用最少的点覆盖所有的点. dp[i][0]表示用i点来覆盖,dp[i][1]表示用孩子节点来覆盖,dp[i][2]表示用父节点来覆盖 (1) dp[i][0] = min(dp[i.son][0], dp[i.son][1], dp[i.son][2]) (2) dp[i][1] = min(dp[i.son][0], dp[i.son][1]) //特判 (3) dp[i]

POJ 3342 Party at Hali-Bula (树形dp 树的最大独立集 判多解 好题)

Party at Hali-Bula Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 5660   Accepted: 2022 Description Dear Contestant, I'm going to have a party at my villa at Hali-Bula to celebrate my retirement from BCM. I wish I could invite all my co

codeforces 455C C. Civilization(树形dp+树的直径+并查集)

题目链接: codeforces 455C 题目大意: 给出一些点,他们之间初始存在一些边,给出两种操作,第一种是查询某个点所在的树的直径,另一种是将两个树合并,要求使合并后的树的直径最小. 题目分析: 首先算取没做操作前的连通块里的树的直径,也就是先dfs一遍,找到深度最大的点,然后从这个点再搜,找到的最远的距离就是这棵树的直径,因为可以证明从根搜深度最大的点一定是树的直径的一个端点,因为它可以通过到达次大的深度的点或者找到与它公共祖先不在根处的获得树的直径. 然后每次合并,我们可以知道得到的

树的点分治 (poj 1741, 1655(树形dp))

poj 1655:http://poj.org/problem?id=1655 题意: 给无根树,  找出以一节点为根,  使节点最多的树,节点最少. 题解:一道树形dp,先dfs 标记 所有节点的子树的节点数. 再dfs  找出以某节点为根的最大子树,节点最少. 复杂度(n) /***Good Luck***/ #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cs

poj 4045 Power Station(初涉树形dp)

http://poj.org/problem?id=4045 大致题意:有n个村庄,求将发电站建在哪一个村庄使得花费最少.这是一个无向无环图.简化一下就是求一个节点使它到其他所有节点的距离和最小. 起初一直在向最短路上靠,但因为节点和边数太大,必定TLE.然后无比强大的啸神随便写了两个dfs就过掉了,简直膜拜.赛后搜了搜题解,发现这是道树形dp.sad,真的要好好刷dp了. 大体思路是将这个无向无环图看做一个树,我们就在这个树上进行动态规划.首先先随便拿一个节点看做根节点(假设节点1),计算出它

Poj 1112 Rebuilding Roads(树形DP+背包)

题意:给你由N个点构成一颗树,问要孤立出一个有P个节点的子树最少需要删除多少条边.N的范围最大为150 N的范围不大,很容易想到在树上面做背包.把每个节点都看成一个背包,然后把每个儿子节点都看成是一组物品.为什么是一组呢,那是因为假设以儿子为根的节点的子树有S个节点,那么就有S+1种情况,要么将这整棵子树舍弃,要么从这个子树中取1-S个节点. 设f[i][j]为以i为根节点的子树,孤立出以i为根节点,一共含有j个节点的子树最少需要删除的边数(不包括删除i和他父亲的连接的那条边(假设i不是根节点)

poj 2342 Anniversary party,树形DP easy

poj 2342 Anniversary party 没有上司的晚会 Ural大学有N个职员,编号为1~N.他们有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.每个职员有一个快乐指数.现在有个周年庆宴会,要求与会职员的快乐指数最大.但是,没有职员愿和直接上司一起与会. 程序名:party 输入格式: 第一行一个整数N.(1<=N<=6000) 接下来N行,第i+1行表示i号职员的快乐指数Ri.(-128<=Ri<=127) 接下来N-1行,每行输入