树形DP(6/6) 1077. 皇宫看守

  • 题意:
    太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。
    皇宫各个宫殿的分布,呈一棵树的形状,宫殿可视为树中结点,两个宫殿之间如果存在道路直接相连,则该道路视为树中的一条边。
    已知,在一个宫殿镇守的守卫不仅能够观察到本宫殿的状况,还能观察到与该宫殿直接存在道路相连的其他宫殿的状况。
    大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。
    可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。
    帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。
  • 假如是看守边,那么一条边之和它两端的节点有关,因此依靠树的遍历,我们只需要考虑当前节点和子节点即可。但该题目要求的是看守点,那么一个点就会和与它相邻的两个节点的相关,即考虑树中结构,当前节点和它的子节点它的父节点的状态都有关系。
  • 而对于某个点,守卫的状态只有放与不放两种状态,于是我们可以根据这两种选择扩展出三种状态。
  • Ⅰ(0) 当前节点放置守卫,那么其子节点可以放置守卫也可以不放。
    Ⅱ(1) 当前节点不放置守卫并且由它的至少一个子节点来看守。
    Ⅲ(2) 当前节点不放置守卫并且由它的父节点来看守,那么其子节点可是可放置或者不放置。
  • 状态转移方程
    f [ u ] [ 0 ] = ∑ v ∈ s o n u m i n { f [ v ] [ 0 ] , f [ v ] [ 1 ] , f [ v ] [ 2 ] } + w i f [ u ] [ 2 ] = ∑ v ∈ s o n u m i n { f [ v ] [ 0 ] , f [ v ] [ 1 ] } f [ u ] [ 1 ] = m i n { f [ u ] [ 2 ] − m i n ( f [ v ] [ 0 ] , f [ v ] [ 1 ] ) + f [ v ] [ 1 ] } , { v ∣ v ∈ s o u u } f[u][0] = \sum_{v\in son_u}min\{f[v][0],f[v][1],f[v][2]\} + w_i \\ f[u][2] = \sum_{v\in son_u}min\{f[v][0],f[v][1]\} \\ f[u][1] = min\{f[u][2] - min(f[v][0],f[v][1]) + f[v][1]\} , \{v | v\in sou_u \} f[u][0]=vsonumin{f[v][0],f[v][1],f[v][2]}+wif[u][2]=vsonumin{f[v][0],f[v][1]}f[u][1]=min{f[u][2]min(f[v][0],f[v][1])+f[v][1]},{vvsouu}
#include <bits/stdc++.h>

using namespace std;

int n,a[10010],head[10010],nxt[10010],to[10010],tot,ind[10010];

void add(int u,int v) {
   to[++tot] = v,nxt[tot] = head[u],head[u] = tot;
   ind[v] ++;
}

bool vis[10010];
int dp[10010][3];

//状态机模型 比较一般的dp都是类似于状态机的模型 不同状态之间来回转移

void dfs(int u) {
   vis[u] = true;
   dp[u][0] = a[u];
   dp[u][1] = 0x3f3f3f3f;
   for(int i = head[u];i;i = nxt[i]) {
   	int v = to[i];
   	if(vis[v]) continue;
   	dfs(v);
   	dp[u][0] += min({dp[v][0],dp[v][1],dp[v][2]});
   	dp[u][2] += min(dp[v][0],dp[v][1]);
   }
   for(int i = head[u];i;i = nxt[i]) {
   	int v = to[i];
   	// if(vis[v]) continue;
   	dp[u][1] = min(dp[u][1],dp[v][0] + dp[u][2] - min(dp[v][0],dp[v][1]));
   	//枚举 在当前点放置哨兵带来的贡献 后面表示从总和中减去当时这部分加的
   }
}

int main() {
   cin >> n;
   for(int i = 1;i <= n;i ++) {
   	int id,cost,k; cin >> id >> cost >> k;
   	a[id] = cost;
   	for(int j = 1;j <= k;j ++) {
   		int to; cin >> to;
   		add(id,to);
   	}
   }
   int root;
   for(int i = 1;i <= n;i ++) {
   	if(!vis[i] && !ind[i]) {
   		root = i;
   		dfs(i);
   	}
   }
   cout << min(dp[root][0],dp[root][1]) << '\n';
   return 0;	
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值