loj10155:数字转换:树的直径问题

题目连接

  • 树形DP专题

题目大意

  • 一个数字n,求n以内的数字之间 “约数和关系” 的最长链;
  • 约数和关系:一个数字 x x x 的约数和为 s [ x ] s[x] s[x]

题目分析

  • 约数和关系 可以想到,如果 s [ x ] < x s[x]s[x]<x ,他们之间可以连一条双向边;题目就转换为了一个树上,求直径。
  • 直径的定义: 树上最长的链。
  • 求树的直径的方法1:用两次dfs来完成
    d f s 1 dfs1 dfs1 从根出发,找到最远的叶子结点 k k k
    d f s 2 dfs2 dfs2 k k k 为根,出发,找到离他最远的点 t t t k k k t t t 之间的距离就是直径。
  • 求直径的做法2:dp的思维来实现
    搜索的过程中,同时记录最长链与次长链,回溯的时候更新。

解题思路1:两次 d f s dfs dfs 求直径

  • 如果 s [ x ] < x s[x]s[x]<x ,他们之间可以连一条双向边;
  • d f s 1 dfs1 dfs1 从根出发,找到最远的叶子结点 k k k
  • d f s 2 dfs2 dfs2 k k k 为根,出发,找到离他最远的点 t t t k k k t t t 之间的距离就是直径。

参考代码1

//loj10155-数字转换
//求树的直径-两次dfs求法 
#include
using namespace std;
const int N=500007;
struct node{
	int nex,to;
}e[N];
int n,ans,su[N];
int nx,lx;	//第一次的最远点和最远距离 
bool b[N];

int las[N],cnt;
void add(int x,int y){
	cnt++;
	e[cnt].nex=las[x];
	e[cnt].to=y;
	las[x]=cnt;
}
void dfs1(int x,int c){   			//找到最远距离的点 nx 
	if(c>lx){
		lx=c;
		nx=x;
	}
	b[x]=1;
	for(int i=las[x];i;i=e[i].nex){
		int y=e[i].to;
		if(!b[y]) dfs1(y,c+1);
	}
	b[x]=0;
}
void dfs2(int x,int c){				//从nx出发,找反向最远点 
	ans=max(ans,c);
	b[x]=1;
	for(int i=las[x];i;i=e[i].nex){
		int y=e[i].to;
		if(!b[y]) dfs2(y,c+1);
	}
	b[x]=0;
}
int main(){
	cin >> n;
	for(int i=1;i<=n;i++){			//求约数和 
		for(int j=2;j*i<=n;j++){
			su[j*i]+=i;
		}	
	}
	for(int i=2;i<=n;i++){          //构图 
		if(i>su[i]){
			add(i,su[i]);
			add(su[i],i);
		}
	}
	dfs1(1,0);
	dfs2(nx,0);
	cout << ans;
	return 0;
} 

解题思路2: d p dp dp 求直径

  • 因为题面要求,对于 x x x的约数和 s [ x ] < x s[x]s[x]<x ,所以可以设定 s [ x ] s[x] s[x] 为父亲节点, x x x 为子节点。
  • dp的思维来实现
    搜索的过程中,同时记录最长链 d 1 [ x ] d1[x] d1[x] 与次长链 d 2 [ x ] d2[x] d2[x],回溯的时候更新。
  • 因为这是一棵不确定根的树,所以最后扫描一次所有的点,记录最大的 d 1 [ x ] + d 2 [ x ] d1[x]+d2[x] d1[x]+d2[x] 就是答案。

参考代码2

//loj10155-数字转换
//求树的直径-dp求法 
#include
using namespace std;
const int N=500007;

int n,ans,su[N];
int d1[N],d2[N];

int main(){
	cin >> n;
	for(int i=1;i<=n;i++){			//求约数和 
		for(int j=2;j*i<=n;j++){
			su[j*i]+=i;
		}	
	}
	// s[y] --> y  是 父亲到儿子的树边 
	for(int y=n;y>=1;y--){
		if(su[y]>=y) continue; 		//约数和必须小于原数 
		int x=su[y];
		if(d1[x]<d1[y]+1){			
			d2[x]=d1[x];			//更新次长边 
			d1[x]=d1[y]+1;			//更新最大边 
		} 
		else if(d2[x]<d1[y]+1)
			d2[x]=d1[y]+1;			//更新次长链 
	}
	for(int i=1;i<=n;i++) if(d1[i]+d2[i]>ans) ans=d1[i]+d2[i];
	cout << ans;
	return 0;
} 


你可能感兴趣的:(树形DP,动态规划,题解)