HDU 4126 Genghis Khan the Conqueror(最小生成树+树形DP)

本文介绍了一道关于最小生成树和树形动态规划的题目,讨论了如何处理图中边权变化对最小生成树价值的影响。当边权变更时,若该边不在最小生成树中,答案不变;若在树中,则需要计算新的最小生成树值。通过以节点为根的DFS,可以求解节点间子树的最短距离,进而得到动态变化后的最小生成树总价值,并求平均值。这种方法实现了O(n^2)的时间复杂度解题。
摘要由CSDN通过智能技术生成

题意:一个N个点的无向图,先生成一棵最小生成树,然后给你Q次询问,每次询问都是x,y,z的形式, 表示的意思是在原图中将x,y之间的边增大(一定是变大的)到z时,此时最小生成树的值是多少。最后求Q次询问最小生成树的平均值。 N<=3000 , Q<=10000。

思路:树形dp好题,想了一天加看题解才做出来。不难想出,如果更改边权的那条边不在最小生成树中,那么这次修改后的结果还是最小生成树的值。

问题是如果修改的这条边恰好在最小生成树中,那么情况就比较复杂,我们假设最小生成树上有两个相邻结点i,j,那么以i为根的子树到以j为根的子树的距离(连接两树的最小边权)为dp[i][j],

那么如果当前修改的是i到j之间的边,那么最后的答案就是最小生成树的值减去修改前的权值加上修改后的权值和dp[i][j]之间的较小值,这个可以用反证法来证,也很简单。

这道题的难点是求dp数组,如果纯暴力来做,时间上无法承受,这道题有一个很巧妙的做法,对于每一个结点u,以它为根进行一次dfs(dfs的是最小生成树上的边),这个dfs求的是u到某一颗子树的最小距离,这样我们就可以在本次dfs时更新所有dp值。

还是拿i和j距离,其实这一步的意思就是求出i中某个节点到j子树的最小距离,那么我们进行n次dfs也就把i,j子树中的所有节点都枚举了一次,也就成功求出了dp[i][j],这样每一次dfs的时间复杂度为O(n),共进行了n次dfs,所以时间复杂度为O(n^2)。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAXN = 3100;
const int INF = 0x3f3f3f3f;
bool vis[MAXN];
int lowc[MAXN], cost[MAXN][MAXN], pre[MAXN];
int n, m;
vector<int> G[MAXN];
int dp[MAXN][MAXN];
int Prim() {
	int ans = 0;
	memset(vis, 0, sizeof(vis));
	vis[1] = true;
	for(int i = 2; i <= n; i++) pre[i] = 1;
	for(int i = 2; i <= n; i++) lowc[i] = cost[1][i];
	for(int i = 2; i <= n; i++) {
		int minc = INF;
		int p = 0;
		for(int j = 1; j <= n; j++) 
			if(!vis[j] && minc>lowc[j]) {
				minc = lowc[j];
				p = j;
			}
		if(minc == INF) return -1; //原图不连通 
		ans += minc;
		vis[p] = true;
		G[p].push_back(pre[p]);
		G[pre[p]].push_back(p);
		for(int j = 1; j <= n; j++)
			if(!vis[j] && lowc[j]>cost[p][j])
				lowc[j] = cost[p][j], pre[j] = p;
	}
	return ans; 
} 
int dfs(int pos, int cur, int fa) {
	int ans = INF;
	if(pos != fa) ans = min(ans, cost[pos][cur]);
	for(int i = 0; i < G[cur].size(); i++) {
		int u = G[cur][i];
		if(u == fa) continue;
		int tmp = dfs(pos, u, cur);
		dp[cur][u] = dp[u][cur] = min(tmp, dp[cur][u]);
		ans = min(ans, tmp);
	}
	return ans;
} 
int main() {
    //freopen("input.txt", "r", stdin);
	while(scanf("%d%d", &n, &m)==2 && n) {
		memset(cost, INF, sizeof(cost));
		memset(dp, INF, sizeof(dp));
		for(int i = 1; i <= n; i++) G[i].clear();
		for(int i = 1, u, v, d; i <= m; i++) {
			scanf("%d%d%d", &u, &v, &d);
			u++; v++;
			cost[u][v] = cost[v][u] = d;
		}
		int ans = Prim();
		for(int i = 1; i <= n; i++) dfs(i, i, -1);
		int q; cin >> q;
		LL sum = 0;
		for(int i = 1, u, v, d; i <= q; i++) {
			scanf("%d%d%d", &u, &v, &d);
			u++; v++;
			if(pre[u]==v || pre[v]==u) sum += ans-cost[u][v]+min(d, dp[u][v]);
			else sum += ans;
		}
		printf("%.4f\n", (double)sum / q);
	}
    return 0;
}


















1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、 4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值