树形dp hdu 4126 Genghis Khan the Conqueror

先用prim算法求一次最小生成树,最小生成树用链式前向星储存起来,然后树形dp预处理,节点i到子树j的最小距离,每次访问改变一条边的值,删去这条边,这个最小生成树被分为两个子树,dfs求得连接这两个子树的最小权值边,将每次访问后的最小生成树权值加起来除以访问次数即得到平均值,

#pragma comment(linker, "/STACK:1024000000,1024000000") 
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <sstream>
#include <cctype>
using namespace std;
typedef long long ll;
typedef pair<int ,int> pii;
#define MEM(a,b) memset(a,b,sizeof a)
#define CLR(a) memset(a,0,sizeof a);
const int inf = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
//#define LOCAL
int mp[3005][3005];
bool vis[3005];
int to[3005];
int dp[3005][3005];
bool flag[3005][3005];
int pnt[3005*2],nxt[3005*2],head[3005],cost[3005*2];
int n,m;
int tmp;
int cnt;
void addedge(int u,int v,int c){
	pnt[cnt]=v;
	nxt[cnt]=head[u];
	cost[cnt]=c;
	head[u]=cnt++;
}
int prime(){
	int ans = 0;
	CLR(vis);
	vis[0]=1;
	for(int i=1;i<n;i++){
		to[i]=0;
	}
	for(int i=1;i<n;i++){
		int mincost = inf;
		int pos;
		for(int j=0;j<n;j++){
			if(!vis[j] && mincost > mp[to[j]][j]){
				mincost = mp[to[j]][j];
				pos = j;
			}
		}
		if(mincost == inf)break;
		ans += mincost;
		vis[pos]=1;
		flag[to[pos]][pos]=1;
		flag[pos][to[pos]]=1;
		addedge(to[pos],pos,mincost);
		addedge(pos,to[pos],mincost);
		for(int j=0;j<n;j++){
			if(!vis[j] && mp[to[j]][j] >  mp[pos][j])
				to[j]=pos;
		}
	}
	return ans;
}
int DP(int u,int pre,int root){
	dp[root][u]=inf;
	if(pre != root)dp[root][u]=mp[root][u];
	for(int i=head[u];~i;i=nxt[i]){
		int v = pnt[i];
		if(v != pre){
			dp[root][u] = min(dp[root][u],DP(v,u,root));
		}
	}
	return dp[root][u];
}
int solve(int u,int pre,int tree){
	int ans = dp[u][tree];
	for(int i=head[u];~i;i=nxt[i]){
		int v = pnt[i];
		if(v!=pre){
			ans = min(solve(v,u,tree),ans);
		}
	}
	return ans;
}
int main()
{
#ifdef LOCAL
	freopen("in.txt", "r", stdin);
//	freopen("out.txt","w",stdout);
#endif
	while(cin >> n >>m && (n+m)){
		double sum = 0;
		for(int i=0;i<n;i++){
			for(int j=0;j<n;j++){
				mp[i][j]=inf;
				flag[i][j]=0;
			}
		}
		cnt = 0;
		MEM(head,-1);
		for(int i=1;i<=m;i++){
			int u,v,c;scanf("%d%d%d",&u,&v,&c);
			mp[u][v]=c;
			mp[v][u]=c;
		}
		tmp = prime();
		for(int i=0;i<n;i++){
			DP(i,-1,i);
		}
		int q;scanf("%d",&q);
		for(int i=1;i<=q;i++){
			int u,v,c;
			scanf("%d%d%d",&u,&v,&c);
			if(!(flag[u][v])){
				sum += tmp;
			}
			else{
				int x = solve(u,v,v);
				sum += min(tmp-mp[u][v]+c,tmp-mp[u][v]+x);
			}
		}
		printf("%.4lf\n",sum/q);
	}
	return 0;
}









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值