POJ 1985 && BOJ 196 && HDU 2196

树型DP维护从有根树的顶点出发到叶子的最长距离和次长距离(要保证通过不同的孩子)

POJ 1985

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int head[40010],cnt;
int dp1[40010],dp2[40010];
int ans=0;
struct Edge{
    int v,c,next;
}edge[80010];
void addedge(int u,int v,int c){
	edge[cnt].v=v;
	edge[cnt].c=c;
	edge[cnt].next=head[u];
	head[u]=cnt++;

	edge[cnt].v=u;
	edge[cnt].c=c;
	edge[cnt].next=head[v];
	head[v]=cnt++;
}
void init(){
	cnt=0;
	memset(head,-1,sizeof(head));
	memset(dp1,0,sizeof(dp1));
	memset(dp2,0,sizeof(dp2));
	ans=0;
}
void dfs(int s,int father){
	int i;
	bool flag=1;
	for(i=head[s];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v!=father){
			dfs(v,s);
			flag=0;
		}
	}
	if(flag){
		dp1[s]=dp2[s]=0;
		return ;
	}
	for(i=head[s];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v!=father){
			if(dp1[v]+edge[i].c>dp1[s]){
				dp2[s]=dp1[s];
				dp1[s]=dp1[v]+edge[i].c;
			}
			else if(dp1[v]+edge[i].c>dp2[s])
				dp2[s]=dp1[v]+edge[i].c;
			ans=max(ans,dp1[s]+dp2[s]);
		}
	}
}
int main(){
    int n,m,i,j,k;
    int u,v,c;
    char dir;
    scanf("%d %d",&n,&m);
	init();
    for(i=1;i<=m;i++){
        scanf("%d %d %d %c",&u,&v,&c,&dir);
		addedge(u,v,c);
	}
	dfs(1,0);
	printf("%d\n",ans);
    return 0;
}

BOJ 196

此题求两个叶子节点的最小距离

树形DP前特判n==2的情况

 

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int head[10010],cnt;
int dp1[10010],dp2[10010];
int ans=0;
int d[10010];
struct Edge{
    int v,c,next;
}edge[20010];
void addedge(int u,int v,int c){
	edge[cnt].v=v;
	edge[cnt].c=c;
	edge[cnt].next=head[u];
	head[u]=cnt++;

	edge[cnt].v=u;
	edge[cnt].c=c;
	edge[cnt].next=head[v];
	head[v]=cnt++;
}
void init(int n){
	cnt=0;
	memset(d,0,sizeof(d));
	memset(head,-1,sizeof(head));
	for(int i=1;i<=n;i++)
		dp1[i]=dp2[i]=60000000;
	ans=60000000;
}
void dfs(int s,int father){
	int i;
	bool flag=1;
	for(i=head[s];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v!=father){
			dfs(v,s);
			flag=0;
		}
	}
	if(flag){
		dp1[s]=dp2[s]=0;
		return ;
	}
	for(i=head[s];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v!=father){
			if(dp1[v]+edge[i].c<dp1[s]){
				dp2[s]=dp1[s];
				dp1[s]=dp1[v]+edge[i].c;
			}
			else if(dp1[v]+edge[i].c<dp2[s])
				dp2[s]=dp1[v]+edge[i].c;
			ans=min(ans,dp1[s]+dp2[s]);
		}
	}
}
int main(){
    int n,i;
    int u,v,c;
    while(scanf("%d",&n)==1){
		if(n==0)break;
		init(n);
		for(i=1;i<n;i++){
		    scanf("%d %d %d",&u,&v,&c);
			addedge(u,v,c);
			d[u]++,d[v]++;
		}
		if(n==2){
			printf("%d\n",c);
			continue;
		}
		for(i=1;i<=n;i++)
			if(d[i]>1){
				dfs(i,0);
				break;
			}
		printf("%d\n",ans);
	}
    return 0;
}

HDU 2196

分两次树形DP,第一次记录的有根树顶点向下所能到达的最长距离和次长距离,并保存最长距离经过的第一个儿子节点。

第二次记录有根树的顶点向上所能达到的最大距离

 

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int head[10010],cnt;
int dp1[10010],dp2[10010],whe[10010];
struct Edge{
    int v,c,next;
}edge[20010];
void addedge(int u,int v,int c){
	edge[cnt].v=v;
	edge[cnt].c=c;
	edge[cnt].next=head[u];
	head[u]=cnt++;

	edge[cnt].v=u;
	edge[cnt].c=c;
	edge[cnt].next=head[v];
	head[v]=cnt++;
}
void init(int n){
	cnt=0;
	memset(head,-1,sizeof(head));
	memset(dp1,0,sizeof(dp1));
	memset(dp2,0,sizeof(dp2));
}
void dfs1(int s,int father){
	int i;
	bool flag=1;
	for(i=head[s];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v!=father){
			dfs1(v,s);
			flag=0;
		}
	}
	if(flag){
		dp1[s]=dp2[s]=0;
		return ;
	}
	for(i=head[s];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v!=father){
			if(dp1[v]+edge[i].c>dp1[s]){
				dp2[s]=dp1[s];
				dp1[s]=dp1[v]+edge[i].c;
				whe[s]=v;
			}
			else if(dp1[v]+edge[i].c>dp2[s])
				dp2[s]=dp1[v]+edge[i].c;
		}
	}
}
void dfs2(int s,int father){
	int i;
	for(i=head[s];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v!=father){
			if(whe[s]!=v){
				if(dp1[s]+edge[i].c>dp1[v]){
					dp2[v]=dp1[v];
					dp1[v]=dp1[s]+edge[i].c;
					whe[v]=s;
				}
				else if(dp1[s]+edge[i].c>dp2[v])
					dp2[v]=dp1[s]+edge[i].c;
			}
			else{
				if(dp2[s]+edge[i].c>dp1[v]){
					dp2[v]=dp1[v];
					dp1[v]=dp2[s]+edge[i].c;
					whe[v]=s;
				}
				else if(dp2[s]+edge[i].c>dp2[v])
					dp2[v]=dp2[s]+edge[i].c;
			}
			dfs2(v,s);
		}
	}
}
int main(){
    int n,i;
    int v,c;
    while(scanf("%d",&n)==1){
		init(n);
		for(i=2;i<=n;i++){
		    scanf("%d %d",&v,&c);
			addedge(i,v,c);
		}
		dfs1(1,0);
		dfs2(1,0);
		for(i=1;i<=n;i++)
			printf("%d\n",dp1[i]);
	}
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值