【算法作业】最少分割回文字符串,开设分公司

server/2024/10/18 23:03:16/

问题描述

  1. 对于一个给定的字符串,给定策略以最少次数将其分割成一些子串,使得某个子串都是回文串。

  2. 某公司拟在某市开一些分公司,公司分布在不同街道,街道结构可以用一棵树来进行表达。为了避免分公司间竞争冲突,两个分公司不允许开在相邻的街道。

    • 若分公司开在不同街道产生的效益相同;

    • 分公司开在不同街道产生的效益不同;

    请分别设计策略使得所开分公司产生的价值最大。

解题思路

T1

  1. 简单编写一个isPalindrome 函数用于判断给定字符串中从 startend 索引范围内的子串是否为回文串。它通过比较首尾字符,逐步向中间移动来进行判断。

  2. minCut 函数接收一个字符串 s 作为输入,然后使用动态规划来计算每个子串的最少分割次数。

  3. 首先,定义了一个长度为 n动态规划数组 dp,其中 dp[i] 表示 s[0:i] 子串的最少分割次数。

  4. 然后,对于字符串中的每个位置 i,判断 s[0:i] 是否为回文串。如果是,说明整个子串已经是回文串,不需要分割,因此 dp[i] 直接设置为 0。

  5. 如果 s[0:i] 不是回文串,则需要考虑如何进行分割。遍历 0i-1 的位置 j,如果 s[j+1:i] 是回文串,那么可以尝试将 s[0:i] 分割成 s[0:j]s[j+1:i] 两个部分,其中 s[0:j] 可以使用 dp[j] 表示已知的最少分割次数。

  6. 最后,返回 dp[n-1],即整个字符串的最少分割次数。

这个算法的时间复杂度是 O( n 2 n^2 n2),其中 n 是字符串的长度,因为需要遍历每个位置来计算最少分割次数。

T2

这个题目要求抽象出一个树,我们可以把街道当成点,街道相邻就指这个树的边。

例如用下面这个邻接矩阵来表示这棵树。

0123456789
011
1111
2111
3111
411
51
61
71
81
91

1. 若分公司开在不同街道产生的效益相同

  1. 状态定义:代码中使用了一个二维数组 dp[i][2] 来表示每个街道的状态,其中 dp[i][0] 表示不选择第 i 个街道时的最大收益,dp[i][1] 表示选择第 i 个街道时的最大收益。

  2. 动态规划:代码中采用了自底向上的动态规划方法。从树的叶子节点开始向上递推,计算每个节点的最大收益。对于每个节点 i,根据其子节点的状态来更新 dp[i][0]dp[i][1]。如果选择了节点 i,则不能选择其相邻的节点,因此 dp[i][1] 的值就是 value[i] 加上所有不选择相邻节点时的最大收益之和。而 dp[i][0] 则是选择与不选择 i 中收益较大的一个。

  3. 最终结果:最后,根据根节点的 dp[0][0]dp[0][1] 中的较大值,即可确定最大收益。

结合了动态规划和树形结构的特点,通过状态转移的方式有效地解决了在树形结构中选择最优解的问题。第一道题其实就是在求解最大独立集问题。时间复杂度为 O( N 2 N^2 N2)

2. 若分公司开在不同街道产生的效益不同

其实在动态规划算法下,这两个题目几乎没有区别。

街道编号0123456789
街道价值1135231435

只需要把选择加入dp数组的节点的cnt改成sum,转为累计价值,而不是个数就行了。

完整代码

T1

#include <iostream>
#include <vector>
#include <string>
#include <climits>using namespace std;// 辅助函数,判断一个子串是否为回文串
bool isPalindrome(const string& s, int start, int end) {while (start < end) {if (s[start] != s[end])return false;start++;end--;}return true;
}int minCut(string s) {int n = s.length();if (n <= 1) return 0;// dp[i] 表示 s[0:i] 子串的最少分割次数vector<int> dp(n, INT_MAX);for (int i = 0; i < n; ++i) {if (isPalindrome(s, 0, i)) {dp[i] = 0; // 整个子串是回文串,不需要分割} else {for (int j = 0; j < i; ++j) {if (isPalindrome(s, j + 1, i)) {// 如果 s[j+1:i] 是回文串,则可以尝试更新 dp[i] 的值dp[i] = min(dp[i], dp[j] + 1);}}}}return dp[n - 1];
}int main() {string s = "abababbbbba";cout << "最少分割次数为:" << minCut(s) << endl;return 0;
}

运行结果

最少分割次数为:2

T2

1. 若分公司开在不同街道产生的效益相同

#include <iostream>using namespace std;// 定义树的节点数
const int N = 10;// 树的邻接矩阵,1 表示节点之间有边相连,0 表示没有边相连
int tree[N][N] = {{0, 1, 1, 0, 0, 0, 0, 0, 0, 0},{1, 0, 0, 1, 1, 0, 0, 0, 0, 0},{1, 0, 0, 0, 0, 1, 1, 0, 0, 0},{0, 1, 0, 0, 0, 0, 0, 1, 1, 0},{0, 1, 0, 0, 0, 0, 0, 0, 0, 1},{0, 0, 1, 0, 0, 0, 0, 0, 0, 0},{0, 0, 1, 0, 0, 0, 0, 0, 0, 0},{0, 0, 0, 1, 0, 0, 0, 0, 0, 0},{0, 0, 0, 1, 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 1, 0, 0, 0, 0, 0}
};// 动态规划求解最多可以开几个分公司
int dp[N][2]; // dp[i][0]表示不选节点i的最大独立集大小,dp[i][1]表示选节点i的最大独立集大小int maxCompanies() {// 根据邻接矩阵进行动态规划for (int i = N - 1; i >= 0; --i) {int cnt0 = 0, cnt1 = 1;for (int j = i + 1; j < N; ++j) {if (tree[i][j]) {cnt0 += max(dp[j][0], dp[j][1]);cnt1 += dp[j][0];}}dp[i][0] = cnt0;dp[i][1] = cnt1;}return max(dp[0][0], dp[0][1]);
}int main() {int result = maxCompanies();cout << "最多可以开的分公司数量为:" << result << endl;return 0;
}

运行结果

最多可以开的分公司数量为:6

2. 若分公司开在不同街道产生的效益不同

#include <iostream>
#include <vector>
#include <algorithm>using namespace std;const int N = 10;// 街道价值
int value[N] = {1, 1, 3, 5, 2, 3, 1, 4, 3, 5};// 树的邻接矩阵
int tree[N][N] = {{0, 1, 1, 0, 0, 0, 0, 0, 0, 0},{1, 0, 0, 1, 1, 0, 0, 0, 0, 0},{1, 0, 0, 0, 0, 1, 1, 0, 0, 0},{0, 1, 0, 0, 0, 0, 0, 1, 1, 0},{0, 1, 0, 0, 0, 0, 0, 0, 0, 1},{0, 0, 1, 0, 0, 0, 0, 0, 0, 0},{0, 0, 1, 0, 0, 0, 0, 0, 0, 0},{0, 0, 0, 1, 0, 0, 0, 0, 0, 0},{0, 0, 0, 1, 0, 0, 0, 0, 0, 0},{0, 0, 0, 0, 1, 0, 0, 0, 0, 0}
};int dp[N][2]; // dp[i][0]表示不选择节点i时的最大收益,dp[i][1]表示选择节点i时的最大收益int maxProfit(int node, int parent) {int sum0 = 0, sum1 = value[node];for (int i = 0; i < N; ++i) {if (tree[node][i] && i != parent) {maxProfit(i, node);sum1 += dp[i][0];sum0 += max(dp[i][0], dp[i][1]);}}dp[node][0] = sum0;dp[node][1] = sum1;return max(sum0, sum1);
}int main() {int result = maxProfit(0, -1); // 根节点为0,父节点为-1cout << "最大收益为:" << result << endl;return 0;
}

运行结果

最大收益为:17

http://www.ppmy.cn/server/28014.html

相关文章

怎么用微信小程序实现远程控制台球室

怎么用微信小程序实现远程控制台球室呢&#xff1f; 本文描述了使用微信小程序调用HTTP接口&#xff0c;实现控制台球室&#xff0c;控制球台上方的照明灯&#xff0c;单台设备可控制多张球台的照明灯。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 …

格瑞威特 | 邀您参加2024全国水科技大会暨技术装备成果展览会

—— 展位号&#xff1a;A13 —— 企业介绍 北京格瑞威特环保设备有限公司成立于2009年&#xff0c;是专业从事设计、研发、销售智能加药计量泵、在线水质分析仪表、便携式水质分析仪表、流量计、液位计、阀门、搅拌机、烟气报警仪、加药装置等各类水处理设备及配件的OEM供服…

HTTP方式在线访问Hadoop HDFS上的文件解决方案

背景&#xff1a; 在做大数据和大模型产品的时候&#xff0c;方式设计的是将文件放在hdfs上进行管理&#xff0c;前几天遇到一个需求&#xff1a;需要通过http的方式去访问hdfs上的问题&#xff0c;以前基本上都是通过hdfs://hadoop01:9000,去访问文件&#xff0c;于是经过一番…

STM32-HAL库12-STM32F407VGT6的PWM主从定时器,发送指定数量脉冲

STM32-HAL库12-STM32F407VGT6的PWM主从定时器&#xff0c;发送指定数量脉冲 一、所用材料 STM32F407VGT6自制双伺服电机控制板&#xff1b; 一川A1系列伺服电机驱动器&#xff08;电0.73KW电机&#xff09;&#xff1b; 二、所学内容 实现PWM发送指定个数脉冲&#xff0c;以…

B树:原理、操作及应用

B树&#xff1a;原理、操作及应用 一、引言二、B树概述1. 定义与性质2. B树与磁盘I/O 三、B树的基本操作1. 搜索&#xff08;B-TREE-SEARCH&#xff09;2. 插入&#xff08;B-TREE-INSERT&#xff09;3. 删除&#xff08;B-TREE-DELETE&#xff09; 四、B树的C代码实现示例五、…

【QEMU系统分析之实例篇(九)】

系列文章目录 第九章 QEMU系统仿真的机器创建分析实例 文章目录 系列文章目录第九章 QEMU系统仿真的机器创建分析实例 前言一、QEMU是什么&#xff1f;二、QEMU系统仿真的机器创建分析实例1.系统仿真的命令行参数2.完成默认设备的设置工作suspend_mux_open()qemu_disable_defa…

ReactNative0.74 版本发布重大更新

React Native 0.74 版本发布&#xff0c;主要更新包括&#xff1a; Yoga 3.0&#xff1a;新版布局引擎带来更稳定的样式处理&#xff0c;并支持基于Web的组件渲染。Yoga 3.0对行反向容器上的边距、填充和边框属性的行为进行了调整&#xff0c;现在与Web保持一致&#xff0c;即不…

unity入门——按钮点击了却无法调用函数

查阅了一番都没有解决问题&#xff0c;最后发现问题是由button的Onclick()事件绑定了代码脚本而不是游戏对象导致的。 如果Onclick()事件绑定的是代码脚本&#xff0c;则下拉框里没有函数&#xff0c;但是点击MonoScript后能手动填入函数名&#xff08;本以为这样就能实现调用…