【洛谷】 P2458 [SDOI2006]保安站岗 (树形dp)
作者:互联网
-
题意:一棵\(n\)个点\(m\)条边的树,每个点都有点权,如果某个点被标记了,那么这个点和它相邻的点会被覆盖,贡献是被标记的这个点的点权,问将所有点覆盖需要花费的最小代价。
-
题解:首先,这题不能和树形dp的模板题等同,因为假如有一颗树\(1-2-3-4\),我们把\(1,4\)标记,那么\(1,2,3,4\)就全被我们覆盖了。根据这个,我们可以的出一个点有3种覆盖情况:
0.由自己覆盖自己,对自己标记
1.由儿子覆盖自己,即儿子要有标记
2.由父亲覆盖自己,即父亲要有标记
\(dp[u][0/1/2]\)分别表示上面三种情况。
第一种情况,因为我自己把自己覆盖了,所以随便父亲儿子怎么标记,都是合法的,\(dp[u][0]=\sum_{v\in son_u}min({dp[v][0],dp[v][1],dp[v][2]})+w[u]\).
第三种情况,我是由父亲覆盖的,那么我自己没有标记,所以我的儿子不能由父亲覆盖,那么\(dp[u][2]=\sum _{v \in son_u}min(dp[v][0],dp[v][1]])\).
第二种情况,我被儿子覆盖了,那么首先,儿子里面一定至少要有一个是自己标记自己的,也就是说\(dp[u][1]\)至少有一个\(dp[v][0]\)转移过来,同时我的所有儿子也一定都必须合法,\(dp[u][1]=\sum _{v \in son_u}min(dp[v][0],dp[v][1]])\).假如\(dp[u][1]\)全是由\(dp[v][1]\)转移而来,那么根据我们前面所说的至少有一个\(dp[v][0]\),就要加上一个\(min(v\in son_u|dp[v][0]-dp[v][1])\).
叶子结点记成\(INF\)即可。
-
代码:
#include <iostream> #include <iomanip> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <stack> #include <queue> #include <vector> #include <map> #include <set> #include <unordered_set> #include <unordered_map> #define ll long long #define db double #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; int gcd(int a,int b){return b?gcd(b,a%b):a;} int lcm(int a,int b){return a/gcd(a,b)*b;} int n; vector<int> edge[N]; int w[N]; ll dp[N][3]; //0:myself 1:son 2:father void dfs(int u,int fa){ dp[u][0]=w[u]; bool flag=false; ll mi=1e18; for(auto to:edge[u]){ if(to==fa) continue; dfs(to,u); dp[u][0]+=min({dp[to][0],dp[to][1],dp[to][2]}); if(dp[to][0]<=dp[to][1]){ flag=true; } mi=min(mi,dp[to][0]-dp[to][1]); dp[u][1]+=min(dp[to][1],dp[to][0]); dp[u][2]+=min(dp[to][0],dp[to][1]); } if(!flag) dp[u][1]+=mi; } int main() { scanf("%d",&n); for(int i=1;i<=n;++i){ int id; scanf("%d",&id); scanf("%d",&w[id]); int m; scanf("%d",&m); for(int i=1;i<=m;++i){ int v; scanf("%d",&v); edge[id].pb(v),edge[v].pb(id); } } dfs(1,-1); printf("%lld",min(dp[1][0],dp[1][1])); return 0; }
标签:洛谷,覆盖,标记,int,P2458,include,dp,define 来源: https://www.cnblogs.com/lr599909928/p/15304220.html