1077. 皇宫看守

题目链接

1077. 皇宫看守

太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫。

皇宫各个宫殿的分布,呈一棵树的形状,宫殿可视为树中结点,两个宫殿之间如果存在道路直接相连,则该道路视为树中的一条边。

已知,在一个宫殿镇守的守卫不仅能够观察到本宫殿的状况,还能观察到与该宫殿直接存在道路相连的其他宫殿的状况。

大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看守,在不同的宫殿安排看守所需的费用不同。

可是陆小凤手上的经费不足,无论如何也没法在每个宫殿都安置留守侍卫。

帮助陆小凤布置侍卫,在看守全部宫殿的前提下,使得花费的经费最少。

输入格式

输入中数据描述一棵树,描述如下:

第一行 \(n\),表示树中结点的数目。

第二行至第 \(n+1\) 行,每行描述每个宫殿结点信息,依次为:该宫殿结点标号 \(i\),在该宫殿安置侍卫所需的经费 \(k\),该结点的子结点数 \(m\),接下来 \(m\) 个数,分别是这个结点的 \(m\) 个子结点的标号 \(r_1,r_2,…,r_m\)

对于一个 \(n\) 个结点的树,结点标号在 \(1\)\(n\) 之间,且标号不重复。

输出格式

输出一个整数,表示最少的经费。

数据范围

\(1≤n≤1500\)

输入样例:

6
1 30 3 2 3 4
2 16 2 5 6
3 5 0
4 4 0
5 11 0
6 5 0

输出样例:

25

样例解释:

\(2、3、4\)结点安排护卫,可以观察到全部宫殿,所需经费最少,为 \(16 + 5 + 4 = 25\)

解题思路

树形dp

  • 状态表示:

    • \(f[i][0]\) 表示未选择节点 \(i\) 且被父节点看到的最小花费
    • \(f[i][1]\) 表示未选择节点 \(i\) 且被子节点看到的最小花费
    • \(f[i][2]\) 表示选择节点 \(i\)
  • 状态计算:

    • \(f[i][0]+=min(f[j][1],f[j][2])\)
    • \(f[i][1]=min(f[j][2]+\sum_{k\neq j}min(f[k][1],f[k][2]))\)
    • \(f[i][2]+=min({f[j][0],f[j][1],f[j][2]})\)
      分析:一个节点被看到无非就三种状态:1.被父节点看到,2.被子节点看到,3.被自己看到,具体分析如下(枚举子节点的情况):
      1.被父节点看到即本身没选择,则子节点要么被自己看到要么被子节点看到
      2.被子节点看到,枚举被哪个子节点看到,其他子节点要么被它的子节点或自己看到
      3.被自己看到,子节点三种情况都有可能
  • 时间复杂度:\(O(n)\)

代码

// Problem: 皇宫看守
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/1079/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=1510;
int n,w[N],f[N][3];//f[i][0]表示选择被父节点看到,f[i][1]表示被子节点看到,f[i][2]表示被自己看到
vector<int> adj[N];
bool v[N];
void dfs(int x)
{
	f[x][1]=1e9;
	f[x][2]=w[x];
	int sum=0;
	for(int y:adj[x])
	{
		dfs(y);
		f[x][0]+=min(f[y][1],f[y][2]);
		f[x][2]+=min({f[y][0],f[y][1],f[y][2]});
		sum+=min(f[y][1],f[y][2]);
	}
	
	for(int y:adj[x])f[x][1]=min(f[x][1],f[y][2]+sum-min(f[y][1],f[y][2]));
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
    	int x;
    	cin>>x;
    	cin>>w[x];
    	int s;
    	cin>>s;
    	while(s--)
    	{
    		int y;
    		cin>>y;
    		adj[x].pb(y);
    		v[y]=true;
    	}
    }
    int root=1;
    while(v[root])root++;
    dfs(root);
    cout<<min(f[root][1],f[root][2]);
    return 0;
}
posted @ 2022-03-18 23:27  zyy2001  阅读(60)  评论(0编辑  收藏  举报