P2016 战略游戏

题面

题目背景

Bob 喜欢玩电脑游戏,特别是战略游戏。但是他经常无法找到快速玩过游戏的办法。现在他有个问题。

题目描述

他要建立一个古城堡,城堡中的路形成一棵无根树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。

注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到。

请你编一程序,给定一树,帮 Bob 计算出他需要放置最少的士兵。

输入格式

第一行一个整数\(n\),表示树中结点的数目。

第二行至第\(n+1\) 行,每行描述每个结点信息,依次为:一个整数\(i\),代表该结点标号,一个自然数 \(k\),代表后面有\(k\) 条无向边与结点 \(i\) 相连。接下来 \(k\) 个整数,分别是每条边的另一个结点标号 \(r_1,r_2,\cdots,r_k\),表示 \(i\)与这些点间各有一条无向边相连。

对于一个\(n\) 个结点的树,结点标号在 \(0\)\(n-1\) 之间,在输入数据中每条边只出现一次。保证输入是一棵树。

输出格式

输出文件仅包含一个整数,为所求的最少的士兵数目。

输入输出样例

输入 #1复制

4
0 1 1
1 2 2 3
2 0
3 0

输出 #1复制

1

说明/提示

数据规模与约定

对于全部的测试点,保证 \(1 \leq n \leq 1500\)

题解

因为是树形结构,并且因为是求最值的问题,所以我们就想到了用树形\(dp\)去做这道题

一般以节点的深浅(子树从小到大)的顺序作为\(DP\)的阶段,\(DP\)的状态表示中,第一维通常是节点的

编号,(代表以该节点为根的子树)

所以我们设\(f[i][0/1]\)表示该节点放还是不放士兵

根据题意,如果当前的节点不放士兵,那么它的子节点必须全部放置士兵,因为要满足士兵可以看到所

有的边。因此我们有状态转移方程\(f[u][0]+=f[v][1]\)(其中v是u的子节点)

如果当前的节点放置

那么我们可以取最小值\(f[u][1]+=min(f[v][0],f[v][1])\)

注意:可以选择任意一个节点为根,答案都是一样的

最后我们的答案就是\(min(f[选择的节点][1],f[选择的节点][0])\)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=50000;
int f[N][2];
int happy[N];
int n;
int din[N];
int ne[N],ver[N],head[N],idx;
void add(int x,int y)
{
    ne[idx]=head[x];
    ver[idx]=y;
    head[x]=idx;
    idx++;
}
 
void dp(int x,int father)
{
    f[x][1]=1;
    for(int i=head[x];i!=-1;i=ne[i])
    {
        int y=ver[i];
        if(y==father)continue;
        dp(y,x);
        f[x][0]+=f[y][1];
        f[x][1]+=min(f[y][0],f[y][1]);
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        int a,k;
		cin>>a>>k;
		for(int i=1;i<=k;i++)
		{
			int q;
			cin>>q;
			add(q,a);
			add(a,q);
		}
    }
    dp(0,-1);
    cout<<min(f[0][0],f[0][1]);
    return 0;
}
 

posted @ 2020-11-24 18:33  邦的轩辕  阅读(99)  评论(0编辑  收藏  举报