题目地址: 1 LA 戳这里 2 hdu 2011_Chengdu_H
题目大意: 给你一颗树 ,告诉你每条边的权值,现在每个节点上的人要到其他的地方去,问所有里程加起来最多可以是多少?
先估计一下上界:
对于每一条边: 它作为割边将图分为两部分,假设这两边的人都尽可能的夸过这条边,这样在这条边上的贡献将达到最大。
先转化为有根树后,取2*min(k,n-k) k是孩子节点的树的size
案例是可以过的,直接敲了 ac 应该是正确的算法 正确性有待证明
LA上直接dfs就可以了
如果是杭电会爆栈 用c++交 + 手动改栈的大小 #proma comment(linker,“/stack1024000000,1024000000”)
代码:
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
struct edge
{
int u;
int v;
int w;
};
int min(int a,int b)
{
return a<b?a:b;
}
edge e[1000005];
vector<int> G[1000005];
// bool vis[1000005];
int p[1000005]; // 记录父亲节点
int tree_size[1000005];
int n;
void dfs(int u,int fa)
{
tree_size[u]++;
int d=G[u].size();
for(int i=0;i<d;i++)
{
int v=G[u][i];
if(v!=fa)
{
dfs(v,p[v]=u);
tree_size[u]+=tree_size[v];
}
}
}
void init()
{
for(int i=0;i<1000005;i++)
G[i].clear();
memset(p,0,sizeof(p));
memset(tree_size,0,sizeof(tree_size));
}
int main()
{
int cas;
cin>>cas;
for(int l=0;l<cas;l++)
{
init();
cin>>n;
int a,b,c;
for(int i=0;i<n-1;i++)
{
scanf("%d%d%d",&a,&b,&c);
G[a-1].push_back(b-1);
G[b-1].push_back(a-1);
e[i].u=a-1;
e[i].v=b-1;
e[i].w=c;
}
p[0]=-1;
dfs(0,-1);
long long ans=0;
for(int i=0;i<n-1;i++)
{
int aa=e[i].u;
int bb=e[i].v;
if(p[aa]==bb)
{
ans+=2*min(tree_size[aa],n-tree_size[aa])*e[i].w;
}
else
{
ans+=2*min(tree_size[bb],n-tree_size[bb])*e[i].w;
}
}
cout<<"Case #"<<l+1<<": "<<ans<<endl;
}
}
关于上界可达正确性的证明 想清楚了子补上来吧