【题解】 树形dp 守卫部署

当我又是水一发博客
题解的意义不在于题解,主要这次可能是很郁闷的一次挂在裸题上…


题目

问题描述

征夷王(征夷大白兔王子青王神人)最近打算灭掉越南(南夷)。征夷王的国家由N个城市、N-1条双向道路组成,其中,任意两个城市之间有且仅有一条通路连接。
然而,万恶的日本(东倭)想在此时偷袭征夷王的国家!
每当一个城市被袭击时,与之有直接道路相连的城市或该城市中若有一只守卫军队,便可以化险为夷。但是,如果与之有直接道路相连的城市和该城市中没有一个城市有一只守卫军队,那么这个城市就悲剧了。征夷王显然不愿意看到自己的国家侵略。所以他在某些城市部署若干只守卫军队,使得任意一个城市被袭击时均可化险为夷。但是,为了尽可能迅捷地灭掉南夷,征夷王希望部署的守卫军队只数最少。现在伟大的王想知道,他最少要部署多少只守卫军队?

输入

输入文件名为Guard.in。
输入第一行一个正整数N,代表征夷王国家拥有的城市个数。
下接N-1行,每行两个正整数Xi和Yi,表示编号为XI和YI的城市之间有一条双向道路。

输出

输出文件名为Guard.out。
输出第一行一个正整数,代表最少要部署的军队个数。

输入输出样例

Guard.in Guard.out
5 2
1 3
5 2
4 3
3 5

数据范围

30%的数据,1<=N<=10。
100%的数据,1<=N<=100000。


题解

显然裸题,类似警卫安排设置状态
需要注意的是,不要多叉转二叉!,耗时太多,直接利用邻接表建多叉树即可
血与泪的教训,可看注释


代码

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

const int maxn = 2 * (1e5 + 10);
int head[maxn], nxt[maxn], pot[maxn], fa[maxn], cnt = 0;
//int bro[maxn], son[maxn];
inline void Line (int x, int y){
    nxt[++cnt] = head[x], pot[cnt] = y, head[x] = cnt;
    nxt[++cnt] = head[y], pot[cnt] = x, head[y] = cnt;
}
/*bool book[maxn] = {0};
queue<int> q;
void build(int U){
    int u, v, fs; q.push(U);
    while (!q.empty()){
    u = q.front(); q.pop();
    for (int i = head[u]; i; i = nxt[i]){
        v = pot[i];
        if (!book[v]){
        if (!son[u]) son[u] = v;
        else bro[fs] = v;
        q.push(v); fs = v;
        }
    }
    book[u] = 1;
    }
    }*/
void getfa(int rt){
    for (int i = head[rt]; i; i = nxt[i])
    if (pot[i] != fa[rt]) fa[pot[i]] = rt, getfa(pot[i]);
}
int inf = 0x3f3f3f3f, f[maxn][3]; //self, son, father
inline int Min (int a, int b){return a < b ? a : b;}
int dp(int rt, int s){
    if (!rt) return inf;
    if (f[rt][s] >= 0) return f[rt][s];
    int mn, v, sum = 0;
    //v = son[rt];
    //while (v){sum += Min(dp(v, 0), dp(v, 1)); v = bro[v];}
    for (int i = head[rt]; i; i = nxt[i]){
    if ((v = pot[i]) == fa[rt]) continue;
    sum += Min(dp(v, 0), dp(v, 1));
    }
    if (!s){
    f[rt][s] = 1;
    //v = son[rt];
    //while (v){f[rt][s] += Min(f[v][0], Min(f[v][1], dp(v, 2))); v = bro[v];}
    for (int i = head[rt]; i; i = nxt[i]){
        if ((v = pot[i]) == fa[rt]) continue;
        f[rt][s] += Min(f[v][0], Min(f[v][1], dp(v, 2)));
    }
    return f[rt][s];
    }
    if(s == 1){
    //v = son[rt];
    mn = inf;
    //while (v){mn = Min(mn, sum - Min(f[v][0], f[v][1]) + f[v][0]); v = bro[v];}
    for (int i = head[rt]; i; i = nxt[i]){
        if ((v = pot[i]) == fa[rt]) continue;
        mn = Min(mn, sum - Min(f[v][0], f[v][1]) + f[v][0]);
    }
    return f[rt][s] = mn;
    }
    return f[rt][s] = sum;
}
int main (){
    freopen ("guard.in", "r", stdin);
    freopen ("guard.out", "w", stdout);

    int n; scanf ("%d", &n);
    for (int i = 1, x, y; i < n; ++i){
    scanf ("%d%d", &x, &y);
    Line(x, y);
    }
    //build(1);
    getfa(1); memset(f, -1, sizeof f);
    printf ("%d", Min(dp(1, 0), dp(1, 1)));

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章