Luogu P2279 [HNOI2003]消防局的設立

這真的是一道SB題。去你的樹形DPgit

咱們看到題目就開始考慮貪心,怎麼搞?spa

一個顯然的思路,每次找出一個深度最大且未被覆蓋的點,而後建一個消防局?code

但這樣的話,動用簡單的人類思惟就能夠知道:我TM的還不如放在它爺爺(父節點的父節點)處呢string

而後考慮有沒有反例。。。。。。30min laterit

怎麼一個反例都沒有?那麼說明這個貪心是正確的了?io

接下來咱們來簡單證實一下貪心的正確性:class

因爲這個點是當前深度最大且未被覆蓋的點,所以全部深度比它深的點都被覆蓋了。next

而後沒有被覆蓋且能夠被這個點覆蓋的點還有如下幾類:static

  1. 它本身(or 兄弟)
  2. 它的父親節點
  3. 它的爺爺節點

而後咱們很容易發現,當把消防站設立在它爺爺處不只能夠覆蓋到全部1,2,3狀況的點,還能夠多往上覆蓋一些點。di

而後咱們直接貪便可。因爲這裏咱們能夠經過BFS的順序直接得出這些點的深度(這樣就直接排好序了)

最後一個一個彈出判斷便可,複雜度\(O(N)\)

CODE

#include<cstdio>
#include<cstring>
#include<cctype>
using namespace std;
const int N=1005;
struct edge
{
    int to,next;
}e[N<<1];
int head[N],cnt,n,x,father[N],q[N],ans;
bool vis[N];
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; while (!isdigit(ch=tc()));
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void double_add(int x,int y)
{
    e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;
    e[++cnt].to=x; e[cnt].next=head[y]; head[y]=cnt;
}
inline void BFS(int x)
{
    register int i,H=0,T=1; q[1]=x; vis[x]=1;
    while (H<T)
    {
        int now=q[++H];
        for (i=head[now];~i;i=e[i].next)
        if (!vis[e[i].to]) q[++T]=e[i].to,vis[e[i].to]=1;
    }
}
inline void reset(int now,int d)
{
    if (d>2) return; vis[now]=1;
    for (register int i=head[now];~i;i=e[i].next)
    reset(e[i].to,d+1);
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i; read(n);
    memset(head,-1,sizeof(head));
    for (i=2;i<=n;++i)
    read(x),double_add(i,x),father[i]=x; BFS(1);
    memset(vis,0,sizeof(vis)); father[1]=1;
    while (n) 
    {
        if (!vis[q[n]]) ++ans,reset(father[father[q[n]]],0); --n;
    }
    return printf("%d",ans),0;
}
相關文章
相關標籤/搜索