1077. 皇宫看守

题目链接

1077. 皇宫看守

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

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

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

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

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

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

输入格式

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

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

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

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

输出格式

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

数据范围

1n1500

输入样例:

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

样例解释:

234结点安排护卫,可以观察到全部宫殿,所需经费最少,为 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]+kjmin(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; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16024104.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(92)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示

喜欢请打赏

扫描二维码打赏

支付宝打赏