【日常学习】【树形DP】codevs3023 魔法禁书目录1:追捕大妈题解

32 篇文章 0 订阅
18 篇文章 0 订阅

题目描述 Description

index饿了,她要找大妈做饭,可是大妈早在错综复杂的学院都市的一处跟炮姐(jq),学院都市是个无向连通图,游走状态的index想要知道对于所有当麻可能在的地点和自己每个游走的地点,平均距离是多少

一句话题意

给定一个含n个点的无向连通图,任意两点间有且仅有一条路径,求两点间距离的平均值,即 ∑disij/(n*n-n) (1≤i≤n,1≤j≤n)

输入描述 Input Description

第一行一个正整数n

随后n-1行每行3个正整数a b c,表示a b两点间有一条长度为c的边

输出描述 Output Description

输出两点间平均距离,保留两位小数。

样例输入 Sample Input

4

1 2 1

2 3 1

2 4 1

样例输出 Sample Output

1.50

数据范围及提示 Data Size & Hint

40%数据中n≤200

60%数据中n≤2000

100%数据中n≤20000

边的长度≤10000

这是我们组内互测时TY君给的题目 我的代码总之是怎么样都过不了的 搜索过程我放在注释里了 哪位神仙能看明白的话烦请不吝指教 鄙人实在蒟蒻···

题目大意如下

树上有n个节点(n<=两万) 给你任意两点间距离 输出平均距离

直接枚举肯定超时,一个好的思路是:
边两端的点数分别为A和B,这条边被经过的次数就是A*B,
它对总的距离和的贡献就是(A*B*此边长度)
求和再除以总路径数N*(N-1)/2
那么就是求每条边两端点的个数 计算一次dfs解决
任取一点为根,在dfs的过程中,对每个点k记录其子树包含的点数(包括其自身)
设点数为a[k],则k的父亲一侧的点数即为N-a[k] 注意乘的时候应该改一改
统计和遍历同时进行 故时间复杂度为O(n)


但是我想我应该是在DFS和最后对边的处理出问题了 TY君的数据都过了 但是codevsA3W7 于是参照着TY君的代码改了一下 最终A了

我们不同之处在于,TY写的显然是树形DP这样子的,一边DFS一边处理 而我是最后扫一遍边集中处理 另外TY君的搜索父子关系很分明,而我用visit数组检验是否为父亲的方法显然要拙劣很多


先把我的代码放上吧:

//D
#include
   
   
    
    
#include
    
    
     
     
#include
     
     
      
      
using namespace std;

const int maxn=40000+10;

int n,ne; 
int p[maxn];
double dp[maxn];
int head[maxn];
struct node
{
	int from,to,next,v;
}e[maxn];//e is edge


void add(int u,int v,int w)
{
	ne++;
	e[ne].from=u;
	e[ne].to=v;
	e[ne].v=w;
	e[ne].next=head[u];
	head[u]=ne;
}
/*
int dfs(int root)
{
	int et=head[root];
	int ans=0;
	while (et!=0)
	{
		if (!visit[e[et].to])
		{
			visit[e[et].to]=1;
			p[e[et].to]=dfs(e[et].to);
			ans+=p[e[et].to];
		}
		et=e[et].next;
	}
	ans++;
	return ans;
}
*/

void dfs(int root,int f=0)
{
	p[root]=1;
	int et=head[root];
	while (et!=0)
	{
		if (e[et].to!=f)
		{
			dfs(e[et].to,root);
			p[root]+=p[e[et].to];
			dp[root]+=dp[e[et].to]+p[e[et].to]*(n-p[e[et].to])*(double)e[et].v;
		}
		et=e[et].next;
	}
	return;
}

int main()
{
	freopen("1.txt","r",stdin);
	//freopen("D.out","w",stdout);
	scanf("%d",&n);
	int u,v,w;
	memset(head,0,sizeof(head));
	for (int i=1;i
      
      
     
     
    
    
   
   

接下来贴上TY的,很简短,用vector存图···虽然我并没有看懂,但是结构体中构造器的运用是很好的

#include<bits/stdc++.h>
using namespace std;
struct edge{
	int from,to,dis;
	edge(){};
	edge(int x,int y,int z){
		from=x,to=y,dis=z;
	}
};
vector<edge>es;
vector<int>G[20005];
int size[20005];
double dp[20005];
int cnt,n;
void add(int u,int v,int w){
	es.push_back(edge(u,v,w));
	G[u].push_back(es.size()-1);
}
void dfs(int u,int f=0){
	size[u]=1;
	for(int i=0;i<G[u].size();++i){
		edge& x=es[G[u][i]];
		if(x.to==f)continue;
		dfs(x.to,u);
		size[u]+=size[x.to];
		dp[u]+=dp[x.to]+size[x.to]*(n-size[x.to])*(double)x.dis;
	}
}
int main(){
	freopen("D.in","r",stdin);
	freopen("D.out","w",stdout); 
	scanf("%d",&n);
	for(int i=1,a,b,c;i<n;++i){
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c);
		add(b,a,c);
	}
	dfs(1);
	int s=(n*n-n)/2;
	printf("%.2lf\n",(double)dp[1]/s);
}

这次比赛真是惨不忍睹,如果这是NOIP我们可怎么办才好,四百分的题目我才60分 

想想第一题,应该算是算法不对 第二题正解是DP 第三题是递推高精 这两道题都没想到 然而至少写个搜索还能得点分数 第三题没想到的原因主要是看题之后没能理清思路找规律吧 第四题 应该说我搜索的思路还是不清晰,没有算法的完整框架,没有想到这是一个树形DP  因此即使算法正确 实现起来问题还是很大 细节处理上也还是漏洞很多 而且代码能力还需要进一步提高

所以还是要加油啊···


——纸上得来终觉浅,绝知此事要躬行


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值