题目
题解
方法一:贪心
对于深度最深的点,肯定需要设置消防站来覆盖它,那么消防站的选取就有多种方案:父亲。祖父。兄弟。显然放在爷爷那里是最优的;
那么算法:每次选择一个深度最深且没有覆盖的点,在他的爷爷那里设置
一种巧妙的O(n):我们使用单调栈,按照bfs的顺序入栈(先进后出),那么最后取到的肯定是深度最大的。
方法二:树形dp
这道题和没有上司的舞会、战略游戏是比较像的,但这里是的距离是2,就比较麻烦了;
参考了别人的blog
用 f[i][j] f [ i ] [ j ] 表示i的子树中所有点已被覆盖,且还能向上覆盖j层的最小花费
用 g[i][j] g [ i ] [ j ] 表示i没有被覆盖,且下面j层也有没被覆盖的最小花费
v=e[i].to;
fa[v]=u;
dfs(v);
f[u][0]=min(f[u][0]+f[v][0],g[u][0]+f[v][1]);
f[u][1]=min(f[u][1]+g[v][0],g[u][1]+f[v][2]);
f[u][2]+=g[v][1];
g[u][0]+=f[v][0];
g[u][1]+=g[v][0];
f[u][1]=min(f[u][1],f[u][2]);
f[u][0]=min(f[u][0],f[u][1]);
g[u][0]=min(f[u][0],g[u][0]);
g[u][1]=min(g[u][1],g[u][0]);
代码
贪心
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
const int maxn=1e4;
const int inf=1e9;
queue <int> q;
int n,m;
struct Edge{
int next,to;
}edge[maxn<<1];
int num_edge,head[maxn],tot[maxn],deep[maxn],fa[maxn];
int ans,zhan[maxn],top;
bool vis[maxn];
void debug(int *A,int len)
{
for (int i=1; i<=len; i++) printf("%d ",A[i]); printf("\n");
}
void add_edge(int from,int to)
{
edge[++num_edge].next=head[from];
edge[num_edge].to=to;
head[from]=num_edge;
}
void add(int x,int y) {add_edge(x,y); add_edge(y,x);}
void bfs()
{
q.push(1); zhan[++top]=1;
while (!q.empty())
{
int now=q.front(); q.pop();
for (int i=head[now]; i!=0; i=edge[i].next)
{
int to=edge[i].to;
if (to!=fa[now])
{
q.push(to); zhan[++top]=to;
}
}
}
}
void ranse(int x,int num)
{
if (num>2) return;
vis[x]=true;
for (int i=head[x]; i!=0; i=edge[i].next)
{
int to=edge[i].to;
ranse(to,num+1);
}
}
int main()
{
scanf("%d",&n);
for (int i=2; i<=n; i++)
{
scanf("%d",&fa[i]);
add(i,fa[i]);
}
bfs();
while (top)
{
int x=zhan[top--];
if (!vis[x])
{
ans++; ranse(fa[fa[x]],0);
}
}
printf("%d\n",ans);
return 0;
}
总结
贪心的思路非常好,在考试中有可能让你骗到大部分分,这要多做一做