省流:被吊打了
自己开的一个坑,死也要填完它。
希望我随手写下的笔记对您的学习有所帮助(也不太可能)。
学习背景
看着那么多dalao会树型DP,我这个小蒟蒻也来挑战一下。
结果还是被dalao吊打(悲
正片开始
了解树型DP
字面意思,树型DP就是在树上做 DP 或是 DP 过程中有树的特征。树形结构拥有递归特征,所以常以子树作为划分阶段的单位。
树型DP的状态常常形如 DP[u][...],表示以 为根的子树……的信息(如最大收益、最小花费等)。
树型DP实现常常采用记忆化搜索,由子节点提供给父节点信息去转移到答案,与 DP 的性质类似(由子节点(子问题)转移到父节点(较大的问题)),也会有父节点为子节点或兄弟节点提供信息的情况。
树的直径
树的直径就是树上最远的两个点之间的距离,连接这两点的路径被称为树的最长链。
例题
洛谷P2610 [ZJOI2012] 旅游
(别看我一上来就丢出一道紫题,这可是水紫(逃))
题目描述
到了难得的暑假,为了庆祝小白在数学考试中取得的优异成绩,小蓝决定带小白出去旅游~~
经过一番抉择,两人决定将 T 国作为他们的目的地。
T 国的国土可以用一个凸 边形来表示,
个顶点表示
个入境/出境口。
T 国包含 个城市,每个城市都是顶点均为
边形顶点的三角形(换而言之,城市组成了关于 T 国的一个三角剖分)。两人的旅游路线可以看做是连接
个顶点中不相邻两点的线段。
为了能够买到最好的纪念品,小白希望旅游路线上经过的城市尽量多。作为小蓝的好友,你能帮帮小蓝吗?
输入格式
每个输入文件中仅包含一个测试数据。
第一行包含两个由空格隔开的正整数 ,
的含义如题目所述。
接下来有 行,每行包含三个整数
, 表示该城市三角形的三个顶点的编号(T 国的
个顶点按顺时间方向从
至
编号)。
输出格式
输出文件共包含一行,表示最多经过的城市数目。(一个城市被当做经过当且仅当其与线路有至少两个公共点)
说明/提示
对于 的数据,
。
对于 的数据,
。
解法(~~怎么感觉开始写题解了~~)
分析
第一眼看到这题没想到怎么用树型DP,连建图连建图都不会,后面想到可以把一个城市看成一个点,如果两个城市有公共边,就在这两个点之间建一条边。
那怎么知道是不是公共边呢?用个 map 统计就好啦(要先把三个数排序哦!)。
这里是可以确定这是一颗树的,那不就可以树型DP了吗。
那这个要求“旅游路上经过的城市尽量多”就可以抽象为“在树上找一条最长路径”,就可以直接求树的直径了。
那么树的直径又要何去求呢?
其实可以找一个点 作为树根(无根树变为有根树),直径的长度就是从点
出发的最长链长度
从点
出发的次长链长度,就是类似于两条链在点
接上了。
但是不知道点 是哪个点怎么办?其实可以随便找一个点作为根(无根树变为有根树),然后对这棵树进行 DFS 维护每个点的从此点出发的最长链长度和从此点出发的次长链长度,最后取个
即可。
设状态
我们设 为从点
出发的最长链长度,
为从点
出发的次长链长度。
转移
设点 的第
个儿子为点
,则有:
若 ,说明有更优的最长链,则将当前的最长链变为次长链,再更新最长链,就是
。
再看,若 且
,说明有更优的次长链,则将当前的次长链扔掉,再更新次长链,就是
。
解
边界为 ,目标解为
,即每个点的最长链长度+次长链长度的最大值即为答案。
CODE(不就是题解吗):
#include<bits/stdc++.h>
using namespace std;
map<pair<int,int>,int>mp;//记录的map
int n,ret,x,y,z,dp[200010][2];
vector<int>g[200010];
void sort1(int &x,int &y,int &z){//这个不必解释,三个数排序
if(x>y){
swap(x,y);
}if(y>z){
swap(y,z);
}if(x>y){
swap(x,y);
}
}void dfs(int u,int x){
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
if(v!=x){
dfs(v,u);//先要DFS
if(dp[v][0]+1>dp[u][0]){//转移
dp[u][1]=dp[u][0],dp[u][0]=dp[v][0]+1;
}else if(dp[v][0]+1>dp[u][1]){
dp[u][1]=dp[v][0]+1;
}
}
}ret=max(ret,dp[u][0]+dp[u][1]);//取max
}int main(){
scanf("%d",&n);
for(int i=1;i<=n+2;i++){
scanf("%d%d%d",&x,&y,&z);
sort1(x,y,z);//排序
if(mp[make_pair(x,y)]!=0){//若之前统计到过这条边了
g[i].push_back(mp[make_pair(x,y)]);
g[mp[make_pair(x,y)]].push_back(i);//与之前map里记录的城市建边
}if(mp[make_pair(y,z)]!=0){//同上
g[i].push_back(mp[make_pair(y,z)]);
g[mp[make_pair(y,z)]].push_back(i);
}if(mp[make_pair(x,z)]!=0){//同上
g[i].push_back(mp[make_pair(x,z)]);
g[mp[make_pair(x,z)]].push_back(i);
}mp[make_pair(x,y)]=i,mp[make_pair(y,z)]=i,mp[make_pair(x,z)]=i;//把标记改为这个城市
}dfs(1,-1);
printf("%d",ret+1);//还要加一,就是点(城市)的数量=边的数量+1
return 0;
}