zoukankan      html  css  js  c++  java
  • AcWing 323 . 战略游戏

    题目传送门

    一、算法分析

    树形\(dp\),与没有上司的舞会具有对称性

    没有上司的舞会题解地址 https://www.acwing.com/solution/acwing/content/7920/

    没有上司的舞会:每条边上最多选择一个点,求最大权值

    战略游戏:每条边上最少选择一条点,求最小权值

    显然我们不能 暴力枚举 树中所有结点 选(1)不选(0) 的状态(时间复杂度 为 \(O(2^n)\)

    因此,会想到采用 分治 思想:
    考虑以结点 \(u\)根节点 的子树,该子树所有的 方案,可以由当前结点 不放哨兵 进行划分

    这也是一种 状态机模型,我简略的绘制一下这个状态机:

    状态表示

    \(f[u][0]\):所有以\(u\)为根的子树中选择,并且不选\(u\)这个点的方案

    \(f[u][1]\):所有以\(u\)为根的子树中选择,并且选\(u\)这个点的方案

    属性
    \(Min\)

    状态计算

    当前\(u\)结点不选,子结点一定选 \(f[u][0]=∑(f[s_i][1])\)

    当前\(u\)结点选,子结点一定可选可不选 \(f[u][1]=∑min(f[s_i][0],f[s_i][1])\)

    时间复杂度 \(O(n)\)

    二、实现代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1510;         //因为题目明确是有向图,无需M=N*2
    int h[N], e[N], ne[N], idx; //邻接表
    int f[N][2];
    /**
     集合:    以结点i为 根节点 的子树,在 i 上放置哨兵(1) 和不放哨兵(0) 的方案
     状态表示: 方案中,放置的哨兵数量 最少
     状态计算:
     */
    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][0] = 0, 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];//节点u不放置哨兵,那么一定要从某一邻接节点放置哨兵
            f[u][1] += min(f[j][0], f[j][1]);//如果节点u放置了哨兵,那么邻接节点可以选择放置或者不放置
        }
    }
    
    int main() {
        //n组数据
        int n;
        //不断读入数据
        while (cin >> n) {
            //多组数据,清空邻接表
            memset(h, -1, sizeof h), idx = 0;
            //清空状态数组
            memset(st, 0, sizeof st);
            //n个节点
            for (int i = 1; i <= n; i++) {
                int x, m;
                scanf("%d:(%d)", &x, &m);//scanf可以按格式读入数据
                while (m--) {
                    int y;
                    cin >> y;
                    //添加一条x->y的有向边
                    //数据样例:1出发可以到达2,3;说明是有向图
                    add(x, y);
                    //标识y不是根
                    st[y] = true;
                }
            }
            //有向图,需要找到出根
            int root = 0;
            while (st[root]) root++;
    
            //从根开始
            dfs(root);
    
            //输出 两个结果取个 min
            printf("%d\n", min(f[root][0], f[root][1]));
        }
        return 0;
    }
    
    
    
  • 相关阅读:
    SpringBoot 整合Redis
    IDEA 修改之前保存的git地址的账号和密码
    SpringBoot 上传文件功能
    JAVA结合 JSON Web Token(JWT) 工具类
    SpringBoot 整合Spring Security框架
    Sublime Text3 设置
    Python 正则表达式
    Django+uWSGI+Nginx 部署网站
    web 设计中引入字体
    HTML 超链接返回上一级
  • 原文地址:https://www.cnblogs.com/littlehb/p/15788291.html
Copyright © 2011-2022 走看看