自定义MCP Server

news/2025/11/7 18:42:37/文章来源:https://www.cnblogs.com/cjsblog/p/19200626

1. MCP Server

引入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>

定义工具

@Service
public class DateService {private static final String[] WEEKDAY_NAMES = { "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日" };/*** 获取今天是星期几** @return 星期描述*/@Tool(description = "获取今天是星期几")public String getTodayWeekday() {System.out.println("getTodayWeekday 被调用");DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();int index = dayOfWeek.getValue() - 1;if (index < 0 || index >= WEEKDAY_NAMES.length) {return "未知星期";}return String.format("今天是%s", WEEKDAY_NAMES[index]);}}@Service
public class NumberService {/*** 判断一个数字是奇数还是偶数* * @param number 要判断的数字* @return 判断结果,返回"奇数"或"偶数"*/@Tool(description = "判断一个数字是奇数还是偶数")public String checkOddOrEven(@ToolParam(description = "要判断的数字") int number) {System.out.println("checkOddOrEven 被调用");if (number % 2 == 0) {return String.format("数字 %d 是偶数", number);} else {return String.format("数字 %d 是奇数", number);}}
}@Service
public class MyToolService {/*** 打印 1-9 的九九乘法表** @return 九九乘法表文本*/@Tool(description = "打印标准九九乘法表")public String printMultiplicationTable() {System.out.println("printMultiplicationTable 被调用");StringBuilder table = new StringBuilder();for (int i = 1; i <= 9; i++) {for (int j = 1; j <= i; j++) {table.append(String.format("%d×%d=%-2d", j, i, i * j));if (j < i) {table.append("  ");}}if (i < 9) {table.append('\n');}}return table.toString();}
}@Service
public class BankService {private static final String BASE_URL = "https://www.abc.xyz.com/payBankInfo/getByBankNo";private final RestClient restClient;private final ObjectMapper objectMapper;public BankService(RestClient.Builder restClientBuilder, ObjectMapper objectMapper) {this.restClient = restClientBuilder.baseUrl(BASE_URL).build();this.objectMapper = objectMapper;}@Tool(description = "通过行号查询开户行信息")public String queryBankInfo(@ToolParam(description = "开户行行号") String bankNo) {System.out.println("queryBankInfo 被调用");try {// 参数验证if (bankNo == null || bankNo.trim().isEmpty()) {return "行号不能为空";}String responseBody = restClient.post().uri(uriBuilder -> uriBuilder.queryParam("bankNo", bankNo.trim()).build()).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).retrieve().body(String.class);// 尝试格式化 JSONtry {Object json = objectMapper.readValue(responseBody, Object.class);return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(json);} catch (Exception e) {// 如果不是 JSON,直接返回原始响应return responseBody;}} catch (RestClientResponseException e) {return String.format("请求失败: HTTP %d - %s", e.getStatusCode().value(), e.getStatusText());} catch (RestClientException e) {return String.format("请求异常: %s", e.getMessage());} catch (Exception e) {return String.format("处理异常: %s", e.getMessage());}}
}

注册工具

package com.example.springaimcpserver;import com.example.springaimcpserver.service.BankService;
import com.example.springaimcpserver.service.DateService;
import com.example.springaimcpserver.service.MyToolService;
import com.example.springaimcpserver.service.NumberService;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;import java.time.LocalDateTime;@SpringBootApplication
public class SpringAiMcpServerApplication {public static void main(String[] args) {SpringApplication.run(SpringAiMcpServerApplication.class, args);}/*** ToolCallback:单个工具,适合简单、独立的工具* ToolCallbackProvider:工具提供者,适合批量注册(通过扫描 @Tool 方法)* 两种方式都会被 Spring AI 识别并注册为 MCP 工具。*/@Beanpublic ToolCallbackProvider customTools(MyToolService myToolService) {return MethodToolCallbackProvider.builder().toolObjects(myToolService).build();}@Beanpublic ToolCallbackProvider customTools2(NumberService numberService, DateService dateService, BankService bankService) {return MethodToolCallbackProvider.builder().toolObjects(numberService, dateService, bankService).build();}public record TextInput(String input) {}public record CalculatorInput(double a, double b, String operation) {}/*** 将输入字母转为大写*/@Beanpublic ToolCallback toUpperCase() {System.out.println("toUpperCase 被调用");return FunctionToolCallback.builder("toUpperCase", (TextInput input) -> input.input().toUpperCase()).inputType(TextInput.class).description("将字母转成大写").build();}/*** 计算器工具 - 支持加减乘除运算*/@Beanpublic ToolCallback calculator() {System.out.println("calculator 被调用");return FunctionToolCallback.builder("calculator", (CalculatorInput input) -> {double a = input.a();double b = input.b();String op = input.operation().toLowerCase().trim();return switch (op) {case "add", "+" -> String.format("%.2f + %.2f = %.2f", a, b, a + b);case "subtract", "-" -> String.format("%.2f - %.2f = %.2f", a, b, a - b);case "multiply", "*", "×" -> String.format("%.2f × %.2f = %.2f", a, b, a * b);case "divide", "/", "÷" -> {if (b == 0) {yield "错误:除数不能为零";}yield String.format("%.2f ÷ %.2f = %.2f", a, b, a / b);}default -> "错误:不支持的操作符。支持的操作:add/+, subtract/-, multiply/*, divide//";};}).inputType(CalculatorInput.class).description("执行基本的数学运算(加法、减法、乘法、除法)。支持的操作:add/+, subtract/-, multiply/*, divide//").build();}/*** 获取当前时间*/@Beanpublic ToolCallback getCurrentTime() {System.out.println("getCurrentTime 被调用");return FunctionToolCallback.builder("getCurrentTime",() -> LocalDateTime.now().toString()).inputType(Void.class).description("获取当前时间").build();}
}

启动MCP服务

spring:application:name: spring-ai-mcp-servermain:banner-mode: offai:mcp:server:name: my-spring-ai-mcp-serverversion: 1.0.0type: asyncsse-endpoint: /ssesse-message-endpoint: /mcp/messagecapabilities:tool: trueresource: trueprompt: truecompletion: true

2. MCP Client

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.5.7</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>spring-ai-mcp-client</artifactId><version>0.0.1-SNAPSHOT</version><name>spring-ai-mcp-client</name><description>spring-ai-mcp-client</description><properties><java.version>17</java.version><spring-ai.version>1.0.3</spring-ai.version><spring-ai-alibaba.version>1.0.0.4</spring-ai-alibaba.version></properties><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-client-webflux</artifactId></dependency><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter-dashscope</artifactId></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>${spring-ai.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-bom</artifactId><version>${spring-ai-alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

application.yml

server:port: 8081spring:application:name: my-spring-ai-mcp-clientmain:web-application-type: noneai:dashscope:api-key: sk-66217287a102487c4c52chat:enabled: trueoptions:model: qwen3-maxtemperature: 0mcp:client:toolcallback:enabled: truesse:connections:server1:url: http://localhost:8080sse-endpoint: /ssemcp-chinese-fortune:url: https://mcp.api-inference.modelscope.netsse-endpoint: /d103803834594b/sse

测试工具调用

package com.example.springaimcpclient;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;@SpringBootApplication
public class SpringAiMcpClientApplication {public static void main(String[] args) {SpringApplication.run(SpringAiMcpClientApplication.class, args);}@Beanpublic CommandLineRunner predefinedQuestions(ChatClient.Builder chatClientBuilder, ToolCallbackProvider tools,ConfigurableApplicationContext context) {// 打印所有可用的工具System.out.println("\n========== 所有可用的工具 ==========");var toolCallbacks = tools.getToolCallbacks();System.out.println("工具总数: " + toolCallbacks.length);for (int i = 0; i < toolCallbacks.length; i++) {var toolDef = toolCallbacks[i].getToolDefinition();System.out.println("\n【工具 " + (i + 1) + "】");System.out.println("  名称: " + toolDef.name());System.out.println("  描述: " + toolDef.description());}System.out.println("=====================================\n");return args -> {String systemPrompt = """你是一个智能助手,拥有多个可用的工具。当用户提问时,你必须优先考虑使用可用的工具来回答问题。只有在没有合适的工具时,才使用你自己的知识来回答。重要规则:1. 如果有工具可以获取实时信息(如时间、日期),必须使用工具2. 如果有工具可以执行操作(如字符串转换),必须使用工具3. 使用工具后,将工具返回的结果展示给用户""";var chatClient = chatClientBuilder.defaultSystem(systemPrompt).defaultToolCallbacks(tools).build();// 测试问题1:当前时间(应该调用getCurrentTime工具)System.out.println("\n>>> QUESTION: 当前时间是多少");System.out.println(">>> ASSISTANT: " + chatClient.prompt("当前时间是多少").call().content());// 测试问题2:字符串转大写(应该调用toUpperCase工具)System.out.println("\n>>> QUESTION: 将abcd转成大写");System.out.println(">>> ASSISTANT: " + chatClient.prompt("将abcd转成大写").call().content());// 测试问题3:计算(应该调用calculator工具)System.out.println("\n>>> QUESTION: 计算 123 加 456 等于多少");System.out.println(">>> ASSISTANT: " + chatClient.prompt("计算 123 加 456 等于多少").call().content());// 测试问题4:今天星期几(应该调用getTodayWeekday工具)System.out.println("\n>>> QUESTION: 今天星期几");System.out.println(">>> ASSISTANT: " + chatClient.prompt("今天星期几").call().content());// 测试问题5:打印九九乘法表(应该调用printMultiplicationTable工具)System.out.println("\n>>> QUESTION: 打印九九乘法表");System.out.println(">>> ASSISTANT: " + chatClient.prompt("打印九九乘法表").call().content());// 测试问题6:查询开户行信息(应该调用queryBankInfo工具)System.out.println("\n>>> QUESTION: 查询行号为303100000629的开户行信息");System.out.println(">>> ASSISTANT: " + chatClient.prompt("查询行号为303100000629的开户行信息").call().content());// 测试问题7:判断奇偶数(应该调用checkOddOrEven工具)System.out.println("\n>>> QUESTION: 数字16是奇数还是偶数");System.out.println(">>> ASSISTANT: " + chatClient.prompt("数字16是奇数还是偶数").call().content());// 测试问题8:帮我算下命,出生时间 2015年 10月19日8点System.out.println("\n>>> QUESTION: 帮我算下命,出生时间 2015年 10月19日8点");System.out.println(">>> ASSISTANT: " + chatClient.prompt("帮我算下命,出生时间 2015年 10月19日8点").call().content());context.close();};}
}

测试结果:

服务器端控制台输出:

getTodayWeekday 被调用
printMultiplicationTable 被调用
queryBankInfo 被调用
checkOddOrEven 被调用

客户端控制台输出:

========== 所有可用的工具 ==========
工具总数: 8【工具 1】名称: spring_ai_mcp_client_server1_calculator描述: 执行基本的数学运算(加法、减法、乘法、除法)。支持的操作:add/+, subtract/-, multiply/*, divide//【工具 2】名称: spring_ai_mcp_client_server1_toUpperCase描述: 将字母转成大写【工具 3】名称: spring_ai_mcp_client_server1_getCurrentTime描述: 获取当前时间【工具 4】名称: spring_ai_mcp_client_server1_queryBankInfo描述: 通过行号查询开户行信息【工具 5】名称: spring_ai_mcp_client_server1_checkOddOrEven描述: 判断一个数字是奇数还是偶数【工具 6】名称: spring_ai_mcp_client_server1_printMultiplicationTable描述: 打印标准九九乘法表【工具 7】名称: spring_ai_mcp_client_server1_getTodayWeekday描述: 获取今天是星期几【工具 8】名称: spring_ai_mcp_client_mcp_chinese_fortune_fortune_teller描述: 根据出生年月日小时分析生辰八字和命理信息来进行算命
=====================================2025-11-07T18:15:26.146+08:00  INFO 57852 --- [my-spring-ai-mcp-client] [           main] c.e.s.SpringAiMcpClientApplication       : Started SpringAiMcpClientApplication in 3.926 seconds (process running for 4.366)>>> QUESTION: 当前时间是多少
>>> ASSISTANT: 当前时间是:2025年11月7日 18:15:27。>>> QUESTION: 将abcd转成大写
>>> ASSISTANT: ABCD>>> QUESTION: 计算 123 加 456 等于多少
>>> ASSISTANT: 123 加 456 等于 579。>>> QUESTION: 今天星期几
>>> ASSISTANT: 今天是星期五。>>> QUESTION: 打印九九乘法表
>>> ASSISTANT: 以下是标准的九九乘法表:```
1×1=1 
1×2=2   2×2=4 
1×3=3   2×3=6   3×3=9 
1×4=4   2×4=8   3×4=12  4×4=16
1×5=5   2×5=10  3×5=15  4×5=20  5×5=25
1×6=6   2×6=12  3×6=18  4×6=24  5×6=30  6×6=36
1×7=7   2×7=14  3×7=21  4×7=28  5×7=35  6×7=42  7×7=49
1×8=8   2×8=16  3×8=24  4×8=32  5×8=40  6×8=48  7×8=56  8×8=64
1×9=9   2×9=18  3×9=27  4×9=36  5×9=45  6×9=54  7×9=63  8×9=72  9×9=81
```>>> QUESTION: 查询行号为303100000629的开户行信息
>>> ASSISTANT: 根据查询结果,行号为 **303100000629** 的开户行信息如下:- **银行名称**: 中国光大银行股份有限公司北京通州支行
- **联系电话**: 80883251
- **生效日期**: 2014-08-06
- **失效日期**: 2999-12-31如需其他帮助,请随时告知!>>> QUESTION: 数字16是奇数还是偶数
>>> ASSISTANT: 数字 16 是偶数。>>> QUESTION: 帮我算下命,出生时间 2015年 10月19日8点
>>> ASSISTANT: 根据您提供的出生时间(2015年10月19日8点),以下是命理分析结果:### 基本信息
- **公历生日**:2015年10月19日
- **农历生日**:乙未年九月初七
- **生肖**:羊
- **星座**:天秤座
- **八字**:乙未 丙戌 戊辰 丙辰
- **纳音五行**:金### 八字解析
- **日主**:戊土(代表自己)
- **年柱**:乙未(比肩)
- **月柱**:丙戌(伤官)
- **日柱**:戊辰(日主坐财)
- **时柱**:丙辰(伤官)### 命理特点
1. **性格特征**:- 日主为戊土,为人诚实稳重,有责任感。- 月柱和时柱均为丙火伤官,表示聪明伶俐、思维活跃,具有创造力和表达能力。- 伤官透出,说明个性独立,不喜欢受约束,有时可能显得有些叛逆。2. **五行分析**:- 八字中土旺(戊土日主,辰戌未三土),火生土(丙火伤官),整体偏燥。- 需要注意调和五行,适当补充金水元素以平衡命局。3. **运势特点**:- 伤官生财,说明有较强的赚钱能力和商业头脑。- 财星在日支,表示财运较好,但需注意理财规划。- 比肩在年柱,代表早年家庭环境较为稳定,但竞争意识较强。4. **注意事项**:- 伤官过旺,需要注意情绪管理和人际关系,避免过于直言不讳。- 土旺之人容易固执,建议多听取他人意见,保持开放心态。### 建议
- **发展方向**:适合从事创意、艺术、技术或商业相关的工作。
- **健康方面**:注意脾胃和消化系统的保养,避免过度劳累。
- **人际关系**:培养耐心和包容心,有助于建立良好的人际网络。以上是基于传统命理学的分析,仅供参考。命运掌握在自己手中,积极努力才是成功的关键!

抓包截图

企业微信截图_20251107173532

SSE方式通信流程:

74d131390da2d6c83d90b43d00f352e2_compress

参考文档:

https://docs.spring.io/spring-ai/reference/getting-started.html

https://github.com/spring-ai-community/awesome-spring-ai

 

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

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

相关文章

英语_错题集_25-11

**答案:** annoying **解析:** 这里需要填入一个形容词来描述“我的小弟弟”的特点。动词 “annoy” 意为“使烦恼”,其形容词形式 “annoying” 表示“令人烦恼的”,符合句意。句子意思是:“我的小弟弟有时真的很…

Ai元人文随想:守护时光的印记

Ai元人文随想:守护时光的印记 岐金兰 论古树保护的文化、生态与精神价值第一章:活态史诗——古树作为历史的无言见证者 在村口,在庙前,在深山幽谷之中,古树以一种超越人类纪年的姿态屹立着。它们不是普通的植物,…

浅谈模拟系列算法

模拟着火/模拟火灾算法 算法名称:模拟火退 研究时间:2023年10月17日19点 算法引入:发现了模拟退火算法中出现的正确性和复杂度问题,故研发模拟火退算法进行优化。 注意到模拟退火算法中出现了较为严重的正确性问题…

第三十六篇

今天是11月7号,今天没课

Ai元人文随想:三值纠缠中的人文关怀

Ai元人文随想:深入探讨三值纠缠思维中蕴含的深刻人文关怀 岐金兰 灵魂的测量仪:三值纠缠思维中的人文之光 在技术理性日益主导、算法试图将人类简化为一行行代码的时代,一种名为“三值纠缠”的思维模型,却悄然进行…

R语言实现多组样本两两t检验的完整教程

t检验的核心思想是通过样本均值与方差的比较,评估两个总体均值是否存在显著差异。当有三个或更多组数据时,单次t检验已不再适用,因此通常的做法是先进行方差齐性检验与单因素方差分析(ANOVA),如果总体差异显著,…

实用指南:TensorFlow深度学习实战(40)——图神经网络(GNN)

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2023QDEZ男人八题赛后总结

这里是QDEZ男人八题赛后总结博客。 这是QDEZ OI 出题组的最后一场公开赛,作为行将就木的退役选手,在此再次提醒大家: 今年欢笑复明年,不知退役在眼前! 比赛于线下机房举行,本次有___人参加比赛,感谢大家的参与。…

学习差的孩子,有必要用学习机吗?

学习差的孩子用学习机是智商税?松鼠AI双线方案给出提分答案 一、打破偏见:学习成绩差的学生,更需要专业 AI 学习机 “学习机是智商税” 的说法,本质上是对 “单一工具依赖” 的否定,而非对 AI 教育价值的否定。对…

CSP-S2023游记

赛前第三天: 白天狂摆一天文化课,整个人都进不到学习状态里,摆摆摆摆摆,晚上在机房里进行了 CSP 动员大会,大家都写下了自己祝福的话或者是自己挂分的秘籍,为了让这些话有灵魂,我加上了两句曹学(最后这两句曹学…

2025苏州驾驶证培训推荐榜:摩托车驾驶证培训、A2驾驶证培训、大车A1驾驶证培训、大车B2驾驶证培训,省心学车选这些

在苏州,驾驶证培训已成为成年人提升生活技能的热门选择,而规模适中、专注服务的小体量培训机构,因教学灵活、关注度高更受青睐。2025 年最新盘点,精选口碑突出的小型驾驶证培训机构,其中苏州从欢机动车贸易有限公…

2025佛山钢管厂家推荐榜:防腐钢管、大口径钢管、螺旋钢管工厂采购选型不踩坑

在工业制造、建筑装修、机械配套等领域,钢管作为基础材料的需求持续稳定,而小型钢管企业凭借灵活适配、精准服务的优势,成为众多采购方的优选。为帮助行业伙伴精准筛选靠谱合作方,本文整理 2025 年优质小型钢管企业…

不谈离散数学基本定理

本文半娱乐向半学术向 先列出定理:1.对于 \(\forall x,y \in \mathbb{Z},x<y\),有 \(x+1\le y\)2.\(\forall a,b\in\mathbb{Z},a<b,x>1\),则有 \(x^a<x^b\)3.\(\forall i\in\{1,2\cdots,n\},a_i\in\mat…

现代Linux网络命令简介

都是字符界面的、很小的。 iftop: /usr/sbin/iftop display bandwidth usage on an interface by host nethogs: /usr/sbin/nethogsNet top tool grouping bandwidth per process nload: /usr/bin/nloaddisplays the c…

深谈王书童变换

首先大家肯定都听过这个名字,毕竟发明者比较著名,他就是和鲍林,泡利三人能得三次诺贝尔奖的著名化学家王书童,不过他这个变换更多的应用于数学领域,所以大多人不太熟悉这个定理,就让我来给大家打通一下! 首先先…

众所周知,高中课内物理需要解微分方程

众所周知,解微分方程作为一种常用的方法是每一个高中生都要掌握的,所以物理课上讲二阶微分方程一点问题都没有吧。 著名物理老师 zjz 曾经说过:微积分,很重要,我带大家推一遍。 所以他就带大家推了一遍。 课内物理…

语文诗歌赏析好题集萃(纯纯的学术向)

QDEZ 某诗社的新社员一进社团就为社团提供了大量素材,让我们跟随他来看一下这些好诗好题,欢迎大家留言自己的答案。 (如有侵权请私聊作者)山火 烬收枝打电,霆动得炬手。 巧瞧焰蚁下,偶有挞痕牛。 午头有梧桐,遍惺…

11.7模拟赛

t1 题面 题意 给一个长为 \(n\) 的序列 \(h\),你要还原一个排列 \(p\)。其中对于任意 \(i\in[1,n]\) 满足 \(\exist \{p_{i},p_{i-1},p_{i+1}\}=h_i\)。求出 \(p\) 的种类数对 \(10^9+7\) 的答案。 对于 \(100\%\) 的…

code first 常用命令

记录一下,之前想找命令的时候找不到  ̄□ ̄|| Enable-Migrations 启用迁移 Add-Migration v_1_0_0 添加迁移 Update-Database 将迁移更新到数据库Update-Database -TargetMigration:v_1_0_0 数据库迁移新指定版本…

动态规划学习/复习笔记

(是时候通过写笔记的方式开始复建OI了) 写在前面:本篇笔记主要适用于有一点DP基础但是实力又比较弱的选手比如我,如果你的水平很高,请手动关闭这篇笔记。 树形DP的复习笔记 首先感觉树形DP属于是所有DP当中最简单的…