话说宁波市的中小学生在镇海中学参加计算机程序设计比赛,比赛之余,他们在镇海中学的各个景点参观。镇海中学共有n个景点,每个景点均有若干学生正在参观。这n个景点以自然数1至n编号,每两个景点的编号均不同。每两个景点之间有且只有一条路径。选择哪个景点集中的学生,才能使所有学生走过的路径之和最小呢?
如果存在多个解,则输出距离1最近的那个点,可以证明答案唯一
输入文件center.in中有若干行:
第一行只有一个正整数n,表示景点数。
第二行有n个1至1000间的整数,这n个整数间互相以一个空格分隔。其中第i个整数表示第i个景点处的学生数。
第三行至第n+1行,每行有三个整数I,j,k,表示景点i和景点j之间有一条长尾k的路径直接连接。其中i<>j,1≤i≤n,1≤j≤n;1≤k≤1000。
输出文件center.out中有二行;
第一行只有一个整数i,表示在第i个景点处集中时,所有学生走过的路径之和最短。
第二行也只有一个整数,表示所有学生走过的路径之和的最小值。
4
3 2 4 1
1 2 5
3 1 6
2 4 4
1
43
【数据限制】
所有的数据均随机生成,且满足:
30%的数据,1≤n≤200。
60%的数据,1≤n≤3000。
100%的数据,1≤n≤100000。
本题一看就是树形DP了吧。。。
好的,考虑怎么DP。
我们记 f [ x ] f[x] f[x]为他所有子节点到他的距离。
然后记 f 1 [ x ] f1[x] f1[x]表示他到根节点的距离。
最后记 n u m [ i ] num[i] num[i]表示他子节点的数量。
先用一次DFS预处理出三个值,然后考虑DP。
a n s [ s t ] = ∑ ( f [ v ] − ( f [ x ] + n u m [ x ] ∗ ( f 1 [ x ] − f 1 [ v ] ) ) ) + ( ( n u m [ v ] − n u m [ x ] ) ∗ ( f 1 [ s t ] − f 1 [ v ] ) ) ; ans[st]=\sum(f[v]-(f[x]+num[x]*(f1[x]-f1[v])))+((num[v]-num[x])*(f1[st]-f1[v])); ans[st]=∑(f[v]−(f[x]+num[x]∗(f1[x]−f1[v])))+((num[v]−num[x])∗(f1[st]−f1[v]));
结合代码理解:`
long long dp(int x)
{
long long s=0;
int st=x;
while (fa[x]>0)
{
int v=fa[x];
s+=1ll*(f[v]-(f[x]+1ll*num[x]*(f1[x]-f1[v])))+(1ll*(num[v]-num[x])*(f1[st]-f1[v]));
x=v;
}
return s+f[st];
}
看不懂的自己YY一下吧,手模一下就懂了。
最后的答案便为: m i n ( a n s [ i ] + f [ i ] ) min(ans[i]+f[i]) min(ans[i]+f[i])了。
最后放出完整的code。
//L.E.M.T专用水印啊!!!!!!!!!!!!!!!!!!!
#include //万能头NB!
using namespace std;
int tot,head[200005],len[200005],next[200005],g[200005],a[100005],n,fa[100005];
long long num[100005],f1[100005],f[100005];
int read()//快读
{
char ch=getchar();
while (ch<'0'||ch>'9')
{
ch=getchar();
}
int s=0;
while (ch>='0'&&ch<='9')
{
s=s*10+(ch-'0');
ch=getchar();
}
return s;
}
void add(int x,int y,int z)
{
tot++,head[tot]=y,len[tot]=z,next[tot]=g[x],g[x]=tot;
}
void dg(int x)
{
num[x]=1ll*a[x];
for (register int i=g[x];i;i=next[i])
{
int v=head[i];
if (v!=fa[x])
{
f1[v]=f1[x]+1ll*len[i];
fa[v]=x;//记录父亲
dg(v);
num[x]+=num[v];
f[x]+=f[v]+num[v]*1ll*len[i];
}
}
}
long long dp(int x)
{
long long s=0;
int st=x;
while (fa[x]>0)
{
int v=fa[x];
s+=1ll*(f[v]-(f[x]+1ll*num[x]*(f1[x]-f1[v])))+(1ll*(num[v]-num[x])*(f1[st]-f1[v]));
x=v;
}
return s+f[st];
}
int main()
{
n=read();
for (register int i=1;i<=n;i++)
{
a[i]=read();
}
for (register int i=1;i<=n-1;i++)
{
int x,y,z;
x=read(),y=read(),z=read();
add(x,y,z);
add(y,x,z);
}
dg(1);
long long ans=0x3f3f3f3f3f3f3f;
int t=0;
for (register int i=1;i<=n;i++)
{
long long sum=dp(i);
if (sum<ans)
{
ans=sum;
t=i;
}
}
printf("%d\n%lld",t,ans);
}
时间复杂度: O ( N l o g N ) O(N logN ) O(NlogN)