P2899 [USACO08JAN]手机网络Cell Phone Network

知识点: 树形DP

原题面

题目要求:

给定一棵无根树,
花费 1价值, 覆盖一个点, 并使该点直接相邻的其他点 也被覆盖
求 将所有点全部覆盖的 最小价值


分析题意:

对于 树上的一个结点, 若要被覆盖,
可被其父, 其子覆盖, 或自己覆盖自己
覆盖一个结点, 只能影响到与其直接相连的结点,
与其距离\(1\)以上的结点 不能被影响, 显然可以DP

对于一棵子树, 若其中所有节点已被覆盖.
现考虑子树根节点的父亲的 覆盖情况, 对此子树的影响

  1. 若子树的根节点 自覆盖 :
    1. 可不花费额外代价的情况下, 将子树根节点的父亲覆盖,
    2. 也可花费额外1代价, 将子树的根节点的父亲覆盖
      从而覆盖子树的根结点, 并影响其二级子节点
  2. 若子树的根节点 未被自己覆盖 :
    即子树的根结点 被其子覆盖
    则 可花费1代价, 将子树的根节点父亲覆盖
    从而覆盖子树的根结点, 并影响其二级子节点

由上, 通过一个节点被覆盖的来源, 可以设计三种状态:
\(f[i][0/1/2]\)表示:
当 第\(i\)个节点为根的子树全部被覆盖,
\(i\)个结点被 其父亲/自己/儿子 覆盖时,
\(i\)个节点为根的子树内 自己覆盖自己的点的个数

对于以结点\(i\) 为根结点的子树,
其上述三个状态的值 可以通过下述方法获得:

  1. \(f[i][0]\) , 即为 \(sum (min(f[j][1], f[j][2])) (j \in son[i])\)
  2. \(f[i][1]\), 将结点i进行自覆盖后, 只能影响到 其直接子节点
    其直接子节点 的状态可以为以上三种任一
    则有: \(f[i][1] = 1 + sum (min(f[j][0], f[j][1], f[j][2])) (j \in son[i])\)
  3. \(f[i][2]\), 根节点i被其子覆盖
    则其子中 必然有至少一进行了自覆盖.
    其它可进行自覆盖, 也可进行 子覆盖 (但不可进行父覆盖)
    • 先抛开 必须有一进行自覆盖这一限制条件,
      则必然选择 子覆盖和自覆盖 中代价更小的
      即: 若满足\(f[j][2] - f[j][1] > 0\), 选择 自覆盖, 否则选择子覆盖

    • 则必须进行自覆盖的子结点, 选择\(f[j][2] - f[j][1]\)最大的 最优
      对于其他的子节点, 当 \(f[j][2] - f[j][1] > 0\) 时, 选择自覆盖, 否则选择子覆盖

    • 可以设计一比较巧妙的算法实现上述过程:
      先使 \(f[i][2] = sum(f[j][1]) (j \in son[i])\), 同时记录所有\(f[j][2] - f[j][1]\)
      之后 对记录的\(f[j][2] - f[j][1]\) 进行升序排序
      然后从头到尾进行选择, 若\(f[j][2] - f[j][1] < 0\), 说明 \(f[j][1] > f[j][2]\)
      此时使 \(f[i][2] += f[j][2] - f[j][1]\), 则可消除之前选择f[j][1]的影响

      当选择了 \(son - 1\) 个或者 \(f[j][2] - f[j][1] >=0\) 时结束选择


#include <cstdio>
#include <ctype.h>
#include <vector>
#include <algorithm>
#define min std :: min
const int MARX = 1e4 + 10;
//=============================================================
struct Edge
{
	int u, v, ne;
} e[MARX << 1];
int N, num, head[MARX];
int f[MARX][3];//0父亲, 1自己, 2儿子 
//=============================================================
inline int read()
{
    int s = 1, w = 0; char ch = getchar();
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') s = -1;
    for(; isdigit(ch); ch = getchar()) w = (w << 1) + (w << 3) + (ch ^ '0');
    return s * w;
}
void addedge(int u, int v)
{
	e[++ num].u = u, e[num].v = v;
	e[num].ne = head[u], head[u] = num;
}
void dfs(int u, int fa)
{
	f[u][1] = 1;//赋初始值 
	std :: vector <int> son;
	for(int i = head[u]; i; i = e[i].ne)
	  if(e[i].v != fa)
	  {
		dfs(e[i].v, u);//优先更新子树 
		f[u][0] += min(f[e[i].v][1], f[e[i].v][2]);//u被父覆盖 
		f[u][1] += min(f[e[i].v][0], min(f[e[i].v][1], f[e[i].v][2]));//u被自覆盖 
		f[u][2] += f[e[i].v][1], son.push_back(f[e[i].v][2] - f[e[i].v][1]);//u子覆盖, 记录所有 f[e[i].v][2] - f[e[i].v][1]
	  }
	std :: sort(son.begin(), son.end());//排序` 
	if(! son.size()) f[u][2] = MARX;//无子 
	for(int i = 0, size = son.size(); i < size - 1; i ++) 
	  if(son[i] < 0) f[u][2] += son[i];//选择替换, 消除影响
	  else break; 
}
//=============================================================
signed main()
{
	N = read();
	for(int i = 1; i < N; i ++)
	{
	  int u = read(), v = read();
	  addedge(u, v), addedge(v, u);
	}
	dfs(1, 0);
	printf("%d", min(f[1][1], f[1][2]));
}
posted @ 2019-12-21 09:02  Luckyblock  阅读(145)  评论(0编辑  收藏  举报