问题描述
对于一个给定的字符串,给定策略以最少次数将其分割成一些子串,使得某个子串都是回文串。
某公司拟在某市开一些分公司,公司分布在不同街道,街道结构可以用一棵树来进行表达。为了避免分公司间竞争冲突,两个分公司不允许开在相邻的街道。
若分公司开在不同街道产生的效益相同;
分公司开在不同街道产生的效益不同;
请分别设计策略使得所开分公司产生的价值最大。
解题思路
T1
简单编写一个
isPalindrome函数用于判断给定字符串中从start到end索引范围内的子串是否为回文串。它通过比较首尾字符,逐步向中间移动来进行判断。minCut函数接收一个字符串s作为输入,然后使用动态规划来计算每个子串的最少分割次数。首先,定义了一个长度为
n的动态规划数组dp,其中dp[i]表示s[0:i]子串的最少分割次数。然后,对于字符串中的每个位置
i,判断s[0:i]是否为回文串。如果是,说明整个子串已经是回文串,不需要分割,因此dp[i]直接设置为 0。如果
s[0:i]不是回文串,则需要考虑如何进行分割。遍历0到i-1的位置j,如果s[j+1:i]是回文串,那么可以尝试将s[0:i]分割成s[0:j]和s[j+1:i]两个部分,其中s[0:j]可以使用dp[j]表示已知的最少分割次数。最后,返回
dp[n-1],即整个字符串的最少分割次数。
这个算法的时间复杂度是 O($n^2$),其中 n 是字符串的长度,因为需要遍历每个位置来计算最少分割次数。
T2
这个题目要求抽象出一个树,我们可以把街道当成点,街道相邻就指这个树的边。
例如用下面这个邻接矩阵来表示这棵树。
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 1 | ||||||||
| 1 | 1 | 1 | 1 | |||||||
| 2 | 1 | 1 | 1 | |||||||
| 3 | 1 | 1 | 1 | |||||||
| 4 | 1 | 1 | ||||||||
| 5 | 1 | |||||||||
| 6 | 1 | |||||||||
| 7 | 1 | |||||||||
| 8 | 1 | |||||||||
| 9 | 1 |
1. 若分公司开在不同街道产生的效益相同
状态定义:代码中使用了一个二维数组
dp[i][2]来表示每个街道的状态,其中dp[i][0]表示不选择第i个街道时的最大收益,dp[i][1]表示选择第i个街道时的最大收益。动态规划:代码中采用了自底向上的动态规划方法。从树的叶子节点开始向上递推,计算每个节点的最大收益。对于每个节点
i,根据其子节点的状态来更新dp[i][0]和dp[i][1]。如果选择了节点i,则不能选择其相邻的节点,因此dp[i][1]的值就是value[i]加上所有不选择相邻节点时的最大收益之和。而dp[i][0]则是选择与不选择i中收益较大的一个。最终结果:最后,根据根节点的
dp[0][0]和dp[0][1]中的较大值,即可确定最大收益。
结合了动态规划和树形结构的特点,通过状态转移的方式有效地解决了在树形结构中选择最优解的问题。第一道题其实就是在求解最大独立集问题。时间复杂度为 O($N^2$)
2. 若分公司开在不同街道产生的效益不同
其实在动态规划算法下,这两个题目几乎没有区别。例如下面这个例子。
| 街道编号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| 街道价值 | 1 | 1 | 3 | 5 | 2 | 3 | 1 | 4 | 3 | 5 |
只需要把选择加入dp数组的节点的cnt改成sum,转为累计价值,而不是个数就行了。
完整代码
T1
1 |
|
运行结果
1 | 最少分割次数为:2 |
T2
1. 若分公司开在不同街道产生的效益相同
1 |
|
运行结果
1 | 最多可以开的分公司数量为:6 |
2. 若分公司开在不同街道产生的效益不同
1 |
|
运行结果
1 | 最大收益为:17 |