1. 程式人生 > >POJ - 4045 Power Station 樹形dp

POJ - 4045 Power Station 樹形dp

題目連結:點選檢視

題意:n個城市節點構成的一棵樹,節點i到節點j的電量損耗為 I*I*R*(i到j的路徑所含邊數),現在要在某個結點上修建一個供電站,使得這個結點到所有其它節點的總損耗量最小。

題解:I*I*R可以提出來,剩下的就求,選擇一個點,求其他點到這個點的距離和最小就可以了,樹形dp,先求一個節點,記錄每個節點子代的個數,逐步向下求即可

#include<iostream>
#include<cstdio>
#include<vector> 
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=50010; 
ll dp[N],son[N];
int n;
ll I,R;
vector<int> v[N];
void dfs1(int u,int fa)
{
	dp[u]=0;
	son[u]=1;
	for(int i=0;i<v[u].size();i++)
	{
		int to=v[u][i];
		if(to==fa) continue;
		dfs1(to,u);
		dp[u]+=dp[to]+son[to];
		son[u]+=son[to];
	}
}
ll minn;
int ans[N],len;
void dfs2(int u,int fa)
{
	minn=min(minn,dp[u]);
	for(int i=0;i<v[u].size();i++)
	{
		int to=v[u][i];
		if(to==fa) continue;
		dp[to]=dp[u]-son[to]+(n-son[to]);// 子代的孩子數就不走這條邊了,剩下的節點要走這條邊
		dfs2(to,u);
	}
}
void dfs3(int u,int fa)
{
	if(minn==dp[u])
	{
		ans[++len]=u;
	}
	for(int i=0;i<v[u].size();i++)
	{
		int to=v[u][i];
		if(to==fa) continue;
		dfs3(to,u);
	}
}
int main()
{
	int T;
	int x,y;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%lld%lld",&n,&I,&R);
		for(int i=1;i<=n;i++)v[i].clear();
		for(int i=1;i<n;i++)
		{
			scanf("%d%d",&x,&y);
			v[x].push_back(y);
			v[y].push_back(x);
		}
		minn=1e18,len=0;
		dfs1(1,0); // 先求節點1
		dfs2(1,0); // 求子代  和  最小值
		dfs3(1,0); // 求 有多少最小值
		sort(ans+1,ans+1+len);
		printf("%lld\n",I*I*R*minn);
		for(int i=1;i<=len;i++)
		printf("%d%c",ans[i]," \n"[i==len]);
		printf("\n");
	}
	return 0;
}