AcWing 323. 战略游戏(树形DP + 状态机DP)

一、问题

在这里插入图片描述

二、分析

1、思路分析

这道题最后问的其实就是,在一棵树中,每个边至少选择一个端点的条件下,我们最少能选择几个端点。

我们以一条边为例子, 两边的端点标号为A 和 B。那么如果A没选的话,B一定选。如果A选了的话,B可以选也可以不选。

如果我们把A和B看作两个物品的话,就会发现A选不选影响到了我们对B的选择。

那么我们就需要把A拿出来分类讨论,这个思路叫做状态机DP。

如果大家对状态机模型不太了解,或者想要深入全面地学习状态机模型的话,可以看作者在算法专栏中写的文章:第四十六章 动态规划——状态机模型

2、状态表示

f [ u ] [ j ] f[u][j] f[u][j]表示以 u u u为根节点的树中选,其中根节点 u u u的状态是 j j j的条件下,并且保证每条边至少选择一个点时,最少选择的点的数目。

3、状态转移

1表示选根节点,0表示不选根节点。
f [ u ] [ 1 ] = ∑ m i n ( f [ s o n ] [ 1 ] , f [ s o n ] [ 0 ] ) f [ u ] [ 0 ] = ∑ f [ s o n ] [ 1 ] f[u][1] = \sum min(f[son][1],f[son][0]) \\ f[u][0]=\sum f[son][1] f[u][1]=min(f[son][1],f[son][0])f[u][0]=f[son][1]

4、循环设计

我们需要遍历整个树,所以之前原本的for循环,由于数据结构的变化,我们需要用DFS代替。

5、初末状态

f [ u ] [ 1 ] = 1 f[u][1] = 1 f[u][1]=1
因为我们选上了根节点,所以我们至少选一个。

最终的结果是: m i n ( f [ r o o t ] [ 0 ] , f [ r o o t ] [ 1 ] ) min(f[root][0], f[root][1]) min(f[root][0],f[root][1])

三、代码

由于我们是从根节点开始找,所以我们需要寻找根节点。因此,我们需要对子节点进行标记,最后没被标记的就是根节点。为了记录这些标记,我们可以开一个数组:bool not_root[N];

#include<bits/stdc++.h>
using namespace std;
const int N = 1510;
int n;
int h[N], e[N], ne[N], idx;
int f[N][2];
bool not_root[N];
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void dfs(int u)
{
    f[u][1] = 1, f[u][0] = 0;
    for(int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        dfs(j);
        f[u][1] = f[u][1] + min(f[j][1], f[j][0]);
        f[u][0] = f[u][0] + f[j][1];
    }
}
int main()
{
    while (~scanf("%d", &n))
    {
        memset(h, -1, sizeof h); idx = 0;
        memset(not_root, 0, sizeof not_root);
        for (int i = 0; i < n; i ++ )
        {
            int a, b, siz;
            scanf("%d:(%d) ", &a, &siz);
            while (siz -- )
            {
                scanf("%d", &b);
                add(a, b);
                not_root[b] = true;
            }
        }
        int root = 0;
        while (not_root[root]) root ++ ;
        dfs(root);
        printf("%d\n", min(f[root][0], f[root][1]));
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值