树状图的打家劫舍

被一道特别像337. 打家劫舍 III的题目搞得心态爆炸。洗了个澡,和舍友讨论了下,复盘才发现其实也没有那么难,冷静下还是能做得出来。这里用类似打家劫舍的描述方法描下这道题。

题目

你是一个专业的小偷,计划偷窃某个地区的房屋,这个地区的房屋布置类似于树状图(无向无环连通图)。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定每间房屋藏有的现金数组money(编号为i(1,2,3,…)的房屋藏有现金money[i-1]),以及每间房屋的相邻关系edges,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的总金额和单间房屋里抢到的最少金额。如果在有多个答案时,输出最少金额最多的答案。

示例1

输入:money=[3, 4, 1, 4, 9], edges=[[0, 1], [0, 2], [1, 3], [2, 4]]

输出:[16, 3]

解释:偷窃 1、4、5 号房屋,总金额 16,最低金额 3

示例2

输入:money=[1, 3, 2, 4, 5, 3], edges=[[1, 4], [2, 4], [3, 5], [4, 6], [5, 6]]

输出:[9, 4]

解释:就总金额看,有两种方案:

① 偷窃 1、2、3、6 号房屋,总金额 9,最低金额1;

② 偷窃 4 、5 号房屋,总金额 9,最低金额 4

个人解法

深度优先算法

删去树状图(无向无环连通图)的任意一点,余下的所有连通图任然是树状图。

未命名绘图

因此我们可以用类似于打家劫舍 III—动态规划的思路来解决问题。

  • 从图中任选一点 P 出发,将 P 看为树状图的根节点;
  • 历遍 P 的所有接邻点,递归计算 P 选中和未选中时的答案。

代码

public class Solution
{
    private int[] money;
    private bool[] visited;
    private bool[,] graph;

    public int[] Rob(int[] money, int[][] edges)
    {
        int n = money.Length;
        this.money = money;
        visited = new bool[n];
        graph = new bool[n, n];

        foreach (var edge in edges)
        {
            graph[edge[0], edge[1]] = true;
            graph[edge[1], edge[0]] = true;
        }

        var res = DFS(0);
        return Max(res[0], res[1]);
    }

    private int[][] DFS(int index)
    {
        visited[index] = true;
        var res = new int[2][];
        res[0] = new[] { 0, int.MaxValue };
        res[1] = new[] { money[index], money[index] };
        for (int i = 0; i < money.Length; i++)
        {
            if (!graph[index, i] || visited[i]) continue;
            var tmp = DFS(i);
            var max = Max(tmp[0], tmp[1]);
            res[0][0] += max[0];
            res[0][1] = Math.Min(res[0][1], max[1]);
            res[1][0] += tmp[0][0];
            res[1][1] = Math.Min(res[1][1], tmp[0][1]);
        }
        return res;
    }

    private int[] Max(int[] a, int[] b)
    {
        return Compare(a, b) >= 0 ? a : b;
    }

    private int Compare(int[] a, int[] b)
    {
        if (a[0] != b[0]) return a[0] - b[0];
        return a[1] - b[1];
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值