洛谷P2016战略游戏 题解

题目链接

分析:

树形DP

f u , 0 f_{u,0} fu,0表示点 u u u不放士兵时 u u u的子树(包括 u u u)最少需要多少个士兵, f u , 1 f_{u,1} fu,1表示点 u u u放士兵时 u u u的子树最少需要多少个士兵。

因为需要每条边的端点都有人,所以若点 u u u不放,则 u u u的每一个儿子都要放,否则 u u u与儿子的连边的两端将都没有士兵,不符合题意。记 s o n u son_u sonu u u u的儿子的集合,则转移方程为:
f u , 0 = ∑ v ∈ s o n u f v , 1 , f u , 1 = ( ∑ v ∈ s o n u m a x ( f v , 0 , f v , 1 ) ) + 1 f_{u,0} = \sum_{v \in son_u} f_{v,1},f_{u,1} = (\sum_{v \in son_u} max(f_{v,0},f_{v,1})) + 1 fu,0=vsonufv,1fu,1=(vsonumax(fv,0,fv,1))+1

最后答案为 m a x ( f r o o t , 0 , f r o o t , 1 ) max(f_{root,0},f_{root,1}) max(froot,0,froot,1)

Code:

#include 
#include 
using namespace std;
const int maxn = 1550;
int n,u,v,k,cnt,last[maxn],f[maxn][2];
struct Edge{
    int v,nxt;
}e[2 * maxn];
int read(){
    int x = 0;
    char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + (c ^ 48),c = getchar();
    return x;
}
void insert(int x,int y){
    cnt ++,e[cnt].v = y,e[cnt].nxt = last[x],last[x] = cnt;
}
void dfs(int u,int fa){
    f[u][1] = 1;
    for(int i = last[u]; i; i = e[i].nxt){
        int v = e[i].v;
        if(v == fa) continue;
        dfs(v,u);
        f[u][0] += f[v][1],f[u][1] += min(f[v][0],f[v][1]);
    }
}
int main(){
    n = read();
    for(int i = 1; i <= n; i ++){
        u = read(),k = read();
        for(int j = 1; j <= k; j ++) v = read(),insert(u,v),insert(v,u);
    }
    dfs(0,-1);//编号从0开始
    cout << min(f[0][0],f[0][1]) << endl;
    return 0;
}

你可能感兴趣的:(洛谷P2016战略游戏 题解)