C++项目——集群聊天服务器项目(十一)服务器异常退出与添加好友业务

news2024/11/24 6:30:43

本节来实现C++集群聊天服务器项目中的服务器异常退出与添加好友业务,一起来试试吧

一、服务器异常退出

在Linux环境下,我们在服务器端使用CTRL+C结束程序执行,即使用CTRL+C让服务器异常退出,这样的后果是本应登录服务器的用户在数据库中在线状态未及时修改为离线,在下次重新登录时会显示用户已登录。

为解决上述问题,编写服务器异常退出函数,只要调用用户操作对象_userModel将所有在线用户状态修改为离线即可

1.1 服务器异常退出代码

代码实现比较简单,在chatservice.hpp中声明函数,在chatservice.cpp中定义

    // 服务端异常,业务重置方法
    void reset();
// 服务端异常,业务重置方法
void ChatService::reset()
{

    // 把所有online用户的状态设置为offline
    _userModel.resetState();
}

在main函数,调用业务模块处理服务器异常退出的函数,signal信号捕获突发事件

#include "chatserver.hpp"
#include "chatservice.hpp"
#include <signal.h>

//处理服务器ctrl+c结束程序后,重置用户user的状态信息
void resetHandler(int){
    ChatService::instance()->reset();
    exit(0);
}

int main()
{
    signal(SIGINT,resetHandler);
    EventLoop loop;
    InetAddress addr("127.0.0.1", 6000);
    ChatServer server(&loop, addr, "ChatServer");

    server.start();
    loop.loop();
    return 0;
}

1.2 功能实现

使用张三的账号登录服务器

张三已经在线

服务器ctrl+c异常退出

数据库张三状态已修改为离线。

服务器异常退出业务验证成功!

二、添加好友

服务器要实现好友聊天,那么就需要添加好友,才可依据好友id号与您的好友进行通信,本节来实现添加好友操作

2.1 添加好友步骤

(1)从json对象中获取用户userid和欲添加的好友friendid

(2)定义FriendModel类对象操作好友表,编写添加好友与查询好友列表两个函数

// 添加好友关系
    bool insert(int userid, int friendid);

    // 返回用户好友列表
    vector<User> query(int userid);

使用FriendModel类对象向数据库好友表中插入好友关系

(3)添加好友成功,返回响应信息、userid和friendid;添加失败,传回响应信息即可

// 添加好友业务             {"msgid":6,"id":22,"name":"Jiao","friendid":13}
void ChatService::addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();               //获取用户id
    int friendid = js["friendid"].get<int>();       //获取好友id

    // 存储消息
    bool state = _friendModel.insert(userid, friendid);
    if(state){
        //添加好友成功!
        json response;
        response["msgid"] = ADD_FRIENG_MSG_ACK;
        response["error"] = 0;
        response["errmsg"]="成功添加好友!";
        response["id"] = userid;
        response["friendid"] = friendid;
        conn->send(response.dump());
    }else{
        //添加好友失败
        json response;
        response["msgid"] = ADD_FRIENG_MSG_ACK;
        response["error"] = 1;
        response["errmsg"]="添加好友失败!";
        conn->send(response.dump());
    }
}

(4)在登录业务中,返回用户离线消息后,也返回用户的好友表,用户可以选择与指定好友进行聊天啦

 // 查询该用户的好友信息并返回
            vector<User> userVec = _friendModel.query(id);
            if (!userVec.empty())
            {
                vector<string> vec2;
                for (User &user : userVec)
                {
                    json js;
                    js["id"] = user.getId();
                    js["name"] = user.getName();
                    js["state"] = user.getState();
                    vec2.push_back(js.dump());
                }
                response["friends"] = vec2;
            }

2.2 添加好友代码

在public.hpp,msgid=6表示添加好友

#ifndef PUBLIC_H
#define PUBLIC_H

/*
server和client的公共文件    MSGID值
*/
enum EnMsgType
{
    LOGIN_MSG = 1, // 1:登录消息
    LOGIN_MSG_ACK, // 2:登录响应消息
    REG_MSG,     // 3:注册消息
    REG_MSG_ACK, // 4:注册响应消息
    ONE_CHAT_MSG,   // 5:聊天消息
    ADD_FRIENG_MSG, // 6:添加好友消息
    ADD_FRIENG_MSG_ACK, // 7:添加好友响应消息
};

#endif

在include/server/model中创建friendmodel.hpp,定义操作好友表的类FriendModel

#ifndef FRIENDMODEL_H
#define FRIENDMODEL_H

#include "user.hpp"
#include <vector>
using namespace std;

// 维护好友信息的操作接口方法
class FriendModel
{
public:
    // 添加好友关系
    bool insert(int userid, int friendid);

    // 返回用户好友列表
    vector<User> query(int userid);
};

#endif

在src/server/friendmodel.cpp中进行实现

#include "friendmodel.hpp"
#include "db.hpp"

// 添加好友关系
bool FriendModel::insert(int userid, int friendid)
{
    char sql[1024] = {0};
    sprintf(sql, "insert into friend values(%d, %d)", userid, friendid);    //向friend表插入好友关系

    MySQL mysql;
    if (mysql.connect())
    {
        mysql.update(sql);
        return true;
    }
    return false;
}

// 返回用户好友列表
vector<User> FriendModel::query(int userid)
{
    char sql[1024] = {0};

    sprintf(sql, "select a.id,a.name,a.state from user a \
    inner join friend b on b.friendid = a.id where b.userid=%d", userid);   //多表联合查询,返回userid的好友列表信息

    vector<User> Friendvec;             //定义vector数组,存放userid的好友对象
    MySQL mysql;
    if (mysql.connect())
    {
        MYSQL_RES *res = mysql.query(sql);
        if (res != nullptr)
        {
            MYSQL_ROW row;
            while((row = mysql_fetch_row(res)) != nullptr)
            {
                User user;
                user.setId(atoi(row[0]));
                user.setName(row[1]);
                user.setState(row[2]);
                Friendvec.push_back(user);
            }
            mysql_free_result(res);
            return Friendvec;
        }
    }
    return Friendvec;
}

业务模块chatservice.hpp创建添加好友业务函数

#ifndef CHATSERVICE_H
#define CHATSERVICE_H

#include <muduo/net/TcpConnection.h>
#include <unordered_map>            //一个消息ID映射一个事件处理
#include <functional>
#include <mutex>
using namespace std;
using namespace muduo;
using namespace muduo::net;

#include "usermodel.hpp"
#include "offlinemessagemodel.hpp"
#include "friendmodel.hpp"
#include "json.hpp"
using json = nlohmann::json;

// 表示处理消息的事件回调方法类型,事件处理器,派发3个东西
using MsgHandler = std::function<void(const TcpConnectionPtr &conn, json &js, Timestamp)>;

// 聊天服务器业务类
class ChatService
{
public:
    // 获取单例对象的接口函数
    static ChatService *instance();
    // 处理登录业务
    void login(const TcpConnectionPtr &conn, json &js, Timestamp time);
    // 处理注册业务
    void reg(const TcpConnectionPtr &conn, json &js, Timestamp time);
    // 处理点对点聊天消息
    void oneChat(const TcpConnectionPtr &conn, json &js, Timestamp time);
    // 添加好友业务
    void addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time);

    // 处理客户端异常退出
    void clientCloseException(const TcpConnectionPtr &conn);
    // 服务端异常,业务重置方法
    void reset();

    // 获取消息对应的处理器
    MsgHandler getHandler(int msgid);

private:
    ChatService(); // 单例

    // 存储消息id和其对应的业务处理方法,消息处理器的一个表,写消息id对应的处理操作
    unordered_map<int, MsgHandler> _msgHandlerMap;
    // 存储用户的通信连接
    unordered_map<int, TcpConnectionPtr> _userConnMap;
    // 定义互斥锁,保证_userConnMap的线程安全
    mutex _connMutex;

    // 数据操作类对象
    UserModel _userModel;                   //用户操作对象
    offlineMsgModel _offlineMsgModel;       //离线消息操作对象
    FriendModel _friendModel;               //好友操作对象
};

#endif

chatservice.cpp中实现,在构造函数中插入消息处理器map表,绑定相应的事件回调

#include "chatservice.hpp"
#include "public.hpp"
#include <muduo/base/Logging.h> //muduo的日志
using namespace std;
using namespace muduo;

// 获取单例对象的接口函数
ChatService *ChatService::instance()
{
    static ChatService service;
    return &service;
}

// 构造方法,注册消息以及对应的Handler回调操作
ChatService::ChatService()
{
    // 用户基本业务管理相关事件处理回调注册
    _msgHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::login, this, _1, _2, _3)});
    _msgHandlerMap.insert({REG_MSG, std::bind(&ChatService::reg, this, _1, _2, _3)});
    _msgHandlerMap.insert({ONE_CHAT_MSG, std::bind(&ChatService::oneChat, this, _1, _2, _3)});
    _msgHandlerMap.insert({ADD_FRIENG_MSG, std::bind(&ChatService::addFriend, this, _1, _2, _3)});
}

// 获取消息对应的处理器
MsgHandler ChatService::getHandler(int msgid)
{
    // 记录错误日志,msgid没有对应的事件处理回调
    auto it = _msgHandlerMap.find(msgid);
    if (it == _msgHandlerMap.end()) // 找不到
    {
        // 返回一个默认的处理器,空操作,=按值获取
        return [=](const TcpConnectionPtr &conn, json &js, Timestamp)
        {
            LOG_ERROR << "msgid:" << msgid << " can not find handler!"; // muduo日志会自动输出endl
        };
    }
    else // 成功的话
    {
        return _msgHandlerMap[msgid]; // 返回这个处理器
    }
}

// 处理登录业务     {"msgid":1,"id":22,"password":"123456"}
void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int id = js["id"].get<int>();
    string pwd = js["password"];
    User user = _userModel.query(id);
    if (user.getId() == id && user.getPwd() == pwd)
    {
        if (user.getState() == "online")
        {
            // 该用户已经登录,不允许重复登录
            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 2;
            response["errmsg"] = "该账号已经登录,请重新输入新账号";
            conn->send(response.dump()); // 回调 ,返回json字符串
        }
        else
        {
            {
                // 登陆成功,记录用户连接
                lock_guard<mutex> lock(_connMutex);
                _userConnMap.insert(make_pair(id, conn));
            }

            // 登录成功,更新用户状态信息
            user.setState("online");
            _userModel.updateState(user);
            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 0;
            response["errmsg"] = "登录成功!";
            response["id"] = user.getId();
            response["name"] = user.getName();
            // 查询该用户是否有离线消息
            vector<string> vec = _offlineMsgModel.query(id);
            if (!vec.empty())
            {
                response["offlinemsg"] = vec;
                // 读取该用户的离线消息后,把该用户的所有离线消息删除掉
                _offlineMsgModel.remove(id);
            }
            // 查询该用户的好友信息并返回
            vector<User> userVec = _friendModel.query(id);
            if (!userVec.empty())
            {
                vector<string> vec2;
                for (User &user : userVec)
                {
                    json js;
                    js["id"] = user.getId();
                    js["name"] = user.getName();
                    js["state"] = user.getState();
                    vec2.push_back(js.dump());
                }
                response["friends"] = vec2;
            }
            conn->send(response.dump()); // 回调 ,返回json字符串
        }
    }
    else
    {
        // 用户名或者密码错误
        json response;
        response["msgid"] = LOGIN_MSG_ACK;
        response["errno"] = 1;
        response["errmsg"] = "用户名或者密码错误";
        conn->send(response.dump()); // 回调 ,返回json字符串
    }
}

// 处理注册业务         {"msgid":3,"name":"Jiao","password":"123456"}
void ChatService::reg(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    string name = js["name"];    // 获取名字
    string pwd = js["password"]; // 获取密码

    User user; // 创建用户对象
    user.setName(name);
    user.setPwd(pwd);
    bool state = _userModel.insert(user); // 新用户的插入
    if (state)                            // 插入成功
    {
        // 注册成功
        json response;
        response["msgid"] = REG_MSG_ACK;
        response["errno"] = 0;
        response["id"] = user.getId();
        conn->send(response.dump()); // 回调 ,返回json字符串
    }
    else // 插入失败
    {
        // 注册失败
        json response;
        response["msgid"] = REG_MSG_ACK;
        response["errno"] = 1;
        conn->send(response.dump()); // 回调 ,返回json字符串
    }
}

// 处理客户端异常退出
void ChatService::clientCloseException(const TcpConnectionPtr &conn)
{
    User user;
    {
        lock_guard<mutex> lock(_connMutex);
        for (auto it = _userConnMap.begin(); it != _userConnMap.end(); it++)    //遍历
        {
            if (it->second == conn)
            {
                user.setId(it->first);
                // 从_userConnMap表中删除用户的连接信息
                _userConnMap.erase(it);
                break;
            }
        }
    }
    // 更新用户的状态信息
    if (user.getId() != -1)
    {
        user.setState("offline");
        _userModel.updateState(user);
    }
}

// 处理点对点聊天消息
void ChatService::oneChat(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int to_id = js["to"].get<int>();                //获取聊天对象id号
    {
        lock_guard<mutex> lock(_connMutex);
        auto it = _userConnMap.find(to_id);         //查看聊天对象是否在线
        if (it != _userConnMap.end())
        {
            // 聊天对象在线,转发消息,服务器主动推动消息给to_id用户
            it->second->send(js.dump());
            return;
        }
    }
    // 聊天对象不在线,插入离线消息表中
    _offlineMsgModel.insert(to_id, js.dump());
}

// 服务端异常,业务重置方法
void ChatService::reset()
{

    // 把所有online用户的状态设置为offline
    _userModel.resetState();
}

// 添加好友业务             {"msgid":6,"id":22,"name":"Jiao","friendid":13}
void ChatService::addFriend(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
    int userid = js["id"].get<int>();               //获取用户id
    int friendid = js["friendid"].get<int>();       //获取好友id

    // 存储消息
    bool state = _friendModel.insert(userid, friendid);
    if(state){
        //添加好友成功!
        json response;
        response["msgid"] = ADD_FRIENG_MSG_ACK;
        response["error"] = 0;
        response["errmsg"]="成功添加好友!";
        response["id"] = userid;
        response["friendid"] = friendid;
        conn->send(response.dump());
    }else{
        //添加好友失败
        json response;
        response["msgid"] = ADD_FRIENG_MSG_ACK;
        response["error"] = 1;
        response["errmsg"]="添加好友失败!";
        conn->send(response.dump());
    }
}


2.3功能验证

登录张三账号,id为13的张三要添加id为15的李四为好友

好友表显示:添加成功

张三再次登录时,会返回张三的好友列表

添加好友业务验证成功!

感兴趣的小伙伴一起来试一下吧~

如果有问题还请及时联系我哦,感谢~

三、项目流程

 1、项目环境搭建 

C++项目——集群聊天服务器项目(一)项目介绍、环境搭建、Boost库安装、Muduo库安装、Linux与vscode配置_c++集群聊天服务器-CSDN博客

2、Json第三方库介绍

C++项目——集群聊天服务器项目(二)Json第三方库-CSDN博客

3、muduo网络库介绍

C++项目——集群聊天服务器项目(三)muduo网络库-CSDN博客

4、MySQL数据库创建

C++项目——集群聊天服务器项目(四)MySQL数据库-CSDN博客

5、网络模块与业务模块代码编写

C++项目——集群聊天服务器项目(五)网络模块与业务模块-CSDN博客

6、MySQL模块编写

C++项目——集群聊天服务器项目(六)MySQL模块-CSDN博客

7、Model层设计、注册业务实现

C++项目——集群聊天服务器项目(七)Model层设计、注册业务实现-CSDN博客

8、用户登录业务

C++项目——集群聊天服务器项目(八)用户登录业务-CSDN博客

9、客户端异常退出业务

C++项目——集群聊天服务器项目(九)客户端异常退出业务-CSDN博客

10、点对点聊天业务

C++项目——集群聊天服务器项目(十)点对点聊天业务-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1562004.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年第十四届蓝桥杯 - 省赛 - Python研究生组 - A.工作时长

题目 数据文件&#xff1a;https://labfile.oss.aliyuncs.com/courses/21074/records.txt Idea 直接通过 datetime 模块加载时间字符串进行格式化&#xff0c;然后对时间列表进行排序&#xff0c;最后两两计算时间差。 Code Python from datetime import datetimetime_lis…

数论与线性代数——整除分块【数论分块】的【运用】【思考】【讲解】【证明(作者自己证的QWQ)】

文章目录 整除分块的思考与运用整除分块的时间复杂度证明 & 分块数量整除分块的公式 & 公式证明公式证明 代码code↓ 整除分块的思考与运用 整除分块是为了解决一个整数求和问题 题目的问题为&#xff1a; ∑ i 1 n ⌊ n i ⌋ \sum_{i1}^{n} \left \lfloor \frac{n}{…

ESP32 引脚分配

请注意&#xff0c;以下引脚分配参考适用于流行的 30 引脚ESP32 devkit v1开发板。 仅输入引脚 GPIO34~39是GPIs–仅输入的管脚。这些引脚没有内部上拉或下拉电阻。它们不能用作输出&#xff0c;因此只能将这些管脚用作输入&#xff1a;GPIO 34、GPIO 35、GPIO 36、GPIO 39 S…

【opencv】教程代码 —features2D(7)根据单应性矩阵估计相机坐标系下的物体位姿...

pose_from_homography.cpp从图像中找到棋盘角点并进行姿态估计 从图像中找到棋盘角点并显示 计算角点在世界坐标系中的位置 读取相机内参和畸变系数并校正图像中的角点 计算从3D点到2D点的单应性矩阵 通过奇异值分解(SVD)优化对旋转矩阵的估计 基于单应矩阵分解及其优化结果&am…

VSCode - 离线安装扩展python插件教程

1&#xff0c;下载插件 &#xff08;1&#xff09;首先使用浏览器打开 VSCode 插件市场link &#xff08;2&#xff09;进入插件主页&#xff0c;点击右侧的 Download Extension 链接&#xff0c;将离线安装包下载下来&#xff08;文件后缀为 .vsix&#xff09; 2&#xff0c;…

Django之REST framework环境搭建

一、环境搭建 Django REST framework是基于Django实现的一个RESTful风格API框架,能够帮助我们快速开发RESTful风格的API 官网:Home - Django REST framework 中文文档:主页 - Django REST framework中文站点 1.1、安装 Python3.8+ pip install django==4.1.1 pip inst…

RISC-V/ARM mcu OpenOCD 调试架构解析

Risc-v/ARM mcu OpenOCD 调试架构解析 最近有使用到risc-v的单片机&#xff0c;所以了解了下risc-v单片机的编译与调试环境的搭建&#xff0c;面试时问到risc-v的调试可参看以下内容。 risc-v根据官方的推荐&#xff0c;调试器服务是选择OpenOCD&#xff0c;DopenOCD(开放片上…

游戏引擎中的声音系统

一、声音基础 1.1 音量 声音振幅的大小 压强p&#xff1a;由声音引起的与环境大气压的局部偏差 1.2 音调 1.3 音色 1.4 降噪 1.5 人的听觉范围 1.6 电子音乐 将自然界中连续的音乐转换成离散的信号记录到内存中 采样 - 量化 - 编码 香农定理&#xff1a;采样频率是信…

ES6学习(五)

文章目录 Module 语法1.1 痛点介绍(1) 异步加载(2) 私密(3) 重名(4) 依赖 1.2 解决方法(1) 解决异步加载问题(2) 解决私密问题(3) 重名解决方法(4) 解决依赖问题 1.3 模块化使用案例 Module 语法 之前js 出现的某些痛点&#xff1a; 在script 中引入的变量名不可以重复&#…

04_Git开发流程

文章目录 Git开发创建阶段开发阶段合并阶段常用指令 Git开发 创建阶段 共建Git仓库&#xff0c;首次使用请使用git clone指令 git clone xxx.git在master/main主干上搭建起基本的项目结构和公共内容&#xff0c;将这些内容push到远程仓库 在Github上创建分支dev&#xff08;de…

2024最新GPT4.0使用教程:GPTs,AI绘画,AI换脸,AI绘画,文档分析一站式解决

一、前言 ChatGPT3.5、GPT4.0、相信对大家应该不感到陌生吧&#xff1f;简单来说&#xff0c;GPT-4技术比之前的GPT-3.5相对来说更加智能&#xff0c;会根据用户的要求生成多种内容甚至也可以和用户进行创作交流。 然而&#xff0c;GPT-4对普通用户来说都是需要额外付费才可以…

HarmonyOS 应用开发之TaskPool和Worker支持的序列化类型

TaskPool和Worker的底层模型为Actor模型&#xff0c;基于Actor模型的内存隔离特性&#xff0c;执行多线程任务和取得结果需要通过跨线程序列化传输。目前支持传输的数据对象可以分为 普通对象 、 可转移对象 、 可共享对象 、 Native绑定对象 四种。 普通对象 普通对象传输采…

《基础设施即代码(IaC)》译者序

随着信息技术的飞速发展&#xff0c;我们对基础设施的理解也在不断深化。传统的基础设施往往被看作是硬件和软件的堆砌&#xff0c;而现在&#xff0c;基础设施的概念已经发生了巨大的变化。在当今这个信息化、数字化的时代&#xff0c;基础设施已经成为了企业和组织运行的核心…

SiteServer 学习笔记 Day04 添加模板

1、添加一个公司简介的模板&#xff0c;显示管理->模版管理->新增内容模板&#xff0c;如下图。 2、在编辑页面如下图。 2.1、代码如下。 <!DOCTYPE HTML> <html lang"en"><head><meta charset"utf-8" /><meta name&quo…

Spire.PDF for .NET【文档操作】演示:合并 PDF 文件并添加页码

搜索了这么多有关 PDF 合并的信息后&#xff0c;很容易发现&#xff0c;无论您在线合并 PDF 文件还是使用 C#/VB.NET 来实现此任务&#xff0c;您都无法逃避对 PDF 文件安全等一些重要问题的担忧&#xff0c;因此需要花费多少时间或者合并后的文件是否支持打印页码等等。不过&a…

嵌入式驱动学习第五周——驱动模块

前言 Linux驱动有两种运行方式&#xff0c;第一种是将驱动编译进Linux内核中&#xff0c;另一种是编译成模块&#xff0c;本篇博客来介绍一下驱动模块。 嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程&#xff0c;未来预计四个月将高强度更新本专栏&#xff0c;喜欢的可…

机器学习实战17-高斯朴素贝叶斯(GaussianNB)模型的实际应用,结合生活中的生动例子帮助大家理解

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下机器学习实战17-高斯朴素贝叶斯(GaussianNB)模型的实际应用&#xff0c;结合生活中的生动例子帮助大家理解。GaussianNB&#xff0c;即高斯朴素贝叶斯模型&#xff0c;是一种基于概率论的分类算法&#xff0c;广泛应…

open Gauss 数据库-04 openGauss数据库日志管理指导手册

发文章是为了证明自己真的掌握了一个知识&#xff0c;同时给他人带来帮助&#xff0c;如有问题&#xff0c;欢迎指正&#xff0c;祝大家万事胜意&#xff01; 目录 前言 openGauss 数据库日志管理 1 实验介绍 2 实验目的 3 系统日志 3.1 运行时日志 3.2 安装卸载时日志…

高效批量剪辑视频,一键设置区间随机抽取画面,批量剪辑视频不再是梦!

在数字世界的浩瀚海洋中&#xff0c;视频内容日益丰富&#xff0c;如何从冗长的视频中快速抓取关键瞬间&#xff0c;将精彩定格为永恒&#xff1f;今天&#xff0c;我们为你带来一款强大的视频剪辑工具&#xff0c;它拥有批量从视频中指定区间随机抽帧并导出保存的功能&#xf…

9.动态规划——3.最大上升子序和

例题——最大上升子序和 分析 需要定义状态 d p [ i ] dp[i] dp[i]&#xff0c;表示前i个元素中&#xff0c;包含第i个元素 a [ i ] a[i] a[i]的最大子序和&#xff0c;则&#xff1a; 若有 j ∈ [ 0 , i − 1 ] j∈[0,i-1] j∈[0,i−1] 当 a [ j ] < a [ i ] a[j]<a[i]…