被一道特别像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];
}
}