保安站岗
Posted stephen-f
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了保安站岗相关的知识,希望对你有一定的参考价值。
分析:
树形dp刚刚入门,这是我做的第一个一个点同时受父亲节点和儿子节点控制的题目。
由于这个题中某一个点放不放保安与父亲和儿子都有关系(因为线段的两个端点嘛),所以我们做题时就要考虑全面。
假设dp数组为f[i][j]f[i][j]:其中f[i][0]f[i][0]表示选择自己(本身这个点),f[i][1]f[i][1]表示自己不选,儿子选(不选本身这个点,而选择这个点的儿子节点),f[i][2]f[i][2]表示自己不选,父亲选(不选本身这个点而选择这个点的父亲节点)
有点啰嗦。。。
看了我的dp数组大家可能有疑问了,树形dp不是用儿子去更新父亲吗?dp不是没有后效性吗?为什么这个点可以看他的父亲?..其实我也是从别人嘴中知道有一种叫做未来计算的东西,就是可以把事先没有发生的但是肯定可以发生的费用加到答案中。
dp转移方程:
设x的儿子节点是v
f[x][0] += min(f[v][1] , min(f[v][2] , f[v][0]))f[x][0]+=min(f[v][1],min(f[v][2],f[v][0]))
f[x][2] += min(f[v][0] , f[v][1])f[x][2]+=min(f[v][0],f[v][1])
注意:
f[x][1]f[x][1]如果有很多儿子怎么办?
当然,自己不选也不一定所有的儿子都选,我们只需要选择一个最优的儿子,我们其实可以记录一个f[v][0] - f[v][1]f[v][0]?f[v][1]的最小值,最后加进去就好了.
if(f[v][0] <= f[v][1]){ f[x][1] += f[v][0]; yes = true; } else { f[x][1] += f[v][1]; minn = min(minn , f[v][0] - f[v][1]); }
代码的话就是这样的。yesyes就是打另一个标记,具体怎么用,看总代码吧,就不赘述了。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 2000; inline int read(){ char ch = getchar(); int f = 1 , x = 0; while(ch > ‘9‘ || ch < ‘0‘){if(ch == ‘-‘)f = -1;ch = getchar();} while(ch >= ‘0‘ && ch <= ‘9‘){x = (x << 1) + (x << 3) + ch - ‘0‘;ch = getchar();} return x * f; } int n,flag,k,m,r; int f[maxn][10],son[maxn]; //f[i][0]:自己选 ,f[i][1]:自己不选,儿子选 ,f[i][2]:自己不选,父亲选 int head[maxn],tot; struct Edge{ int from,to,next; }edge[maxn << 1]; void add(int u,int v){ edge[++tot].from = u; edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot; } void dfs(int x,int fa){ f[x][0] = son[x]; for(int i=head[x];i;i=edge[i].next){ int v = edge[i].to; if(v != fa) dfs(v , x); } bool yes = false , have = false; int minn = 1e9 ; for(int i=head[x];i;i=edge[i].next){ int v = edge[i].to; have = true; f[x][0] += min(f[v][1] , min(f[v][2] , f[v][0])); f[x][2] += min(f[v][0] , f[v][1]); if(f[v][0] <= f[v][1]){ f[x][1] += f[v][0]; yes = true; } else { f[x][1] += f[v][1]; minn = min(minn , f[v][0] - f[v][1]); } } if(!yes) f[x][1] += minn; if(!have) f[x][1] = 1e9; } int main(){ n = read(); for(int i=1;i<=n;i++){ flag = read(); k = read(); m = read(); son[flag] = k; if(m != 0){ for(int j=1;j<=m;j++){ r = read(); add(flag , r); add(r , flag); } } } memset(f , 0 , sizeof(f)); dfs(1 , 0); printf("%d ",min(f[1][1] , f[1][0])); return 0; }
以上是关于保安站岗的主要内容,如果未能解决你的问题,请参考以下文章