Acwing 323. 战略游戏

参考题目:Acwing 323. 战略游戏

题意

给定一个树,要使每条边上至少选一个点,求最少需要选几个点。

算法:树形dp

时间复杂度: O ( n ) O(n) O(n) (每个点只会被搜一次)

f 数组含义:

  • f[i][1]:在 i 号点放人的所有方案中的最小花费。
  • f[i][0]:在 i 号点不放人的所有方案中的最小花费。
  • 根据定义,答案为 min(f[root][1], f[root][0]) ,即根节点不放人和放人的所有方案中的最小值。

st 数组含义:

  • st[i] = true,表示该点有父节点。
  • st[i] = false,表示该点没有父节点。

递归逻辑:

  • DFS 先向下预处理出来子节点的方案,回溯时使用子节点的方案去更新父节点的方案。

转移方程:

  • u:父节点编号,j:子节点编号。
  • f[u][0] += f[j][1]:父节点不放人,那么子节点必须放人。
  • 因为,如果子节点不放人,u -> j 这条边就不符合条件(这条边的两端点都没放人)。所以,在父节点不放人的情况下,子节点必须放人。
  • f[u][1] += min(f[j][1],f[j][0]):父节点放人,取子节点放人或不放人两种中的最小值。
  • 如果父节点放人,其子节点放不放人皆可,所以取其最小的一个。
代码
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1510;

int n;
int f[N][2];
int h[N], e[N], ne[N], idx;
bool st[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;
    
    // 遍历所有的子节点。
    for (int i = h[u]; ~i; i = ne[i])
    {
    	// 取出来子节点编号。
        int j = e[i];
        
        // 预处理出子节点花费。
        dfs(j);
        
        // 转移方程。
        f[u][0] += f[j][1];
        f[u][1] += min(f[j][1], f[j][0]);
    }
}

int main()
{
    while (scanf("%d", &n) == 1)
    {
    	// 多组输入,每次要重新初始化。
        memset(h, -1, sizeof h);
        memset(st, 0, sizeof st);
        memset(f, 0, sizeof f);
        idx = 0;
        
        // 存图。
        for (int i = 0; i < n; i ++ )
        {
            int id, m;
            scanf("%d:(%d)", &id, &m);
            while (m -- )
            {
                int ver;
                scanf("%d", &ver);
                add(id, ver);
                st[ver] = true;
            }
        }
        
        // 有向边,需要找到根节点。
        int root = 0;
        while (st[root]) root ++ ;
        
        // 从根节点开始搜索。
        dfs(root);
        
        printf("%d\n", min(f[root][1], f[root][0]));
    }
    return 0;
}

参考资料
https://www.acwing.com/video/419/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值