题目
Description
话说宁波市的中小学生在镇海中学参加计算机程序设计比赛,比赛之余,他们在镇海中学的各个景点参观。镇海中学共有n个景点,每个景点均有若干学生正在参观。这n个景点以自然数1至n编号,每两个景点的编号均不同。每两个景点之间有且只有一条路径。选择哪个景点集中的学生,才能使所有学生走过的路径之和最小呢?
Input
输入文件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。
Output
输出文件center.out中有二行;
第一行只有一个整数i,表示在第i个景点处集中时,所有学生走过的路径之和最短。
第二行也只有一个整数,表示所有学生走过的路径之和的最小值
解题思路
30分,考试代码,Floyd
100分,正解,树型动态规划。只需先算出一次以1为根节点的树的所有学生数量,和路径。然后沿着这棵树,求出假设子节点为“根节点”的树时的值【一边加,一边减】。然后枚举求出最大值即可。
代码(30分)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,a[10001],x,y,t[10001][10001];
int main()
{
scanf("%d",&n);
memset(t,127/3,sizeof(t));
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
for (int i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
scanf("%d",&t[x][y]);
t[y][x]=t[x][y];
}
for (int m=1;m<=n;m++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) if(i!=j)
t[i][j]=min(t[i][j],t[i][m]+t[m][j]);
int ans=1e9,minn=-1;
for (int i=1;i<=n;i++)
{
int ma=0;
for (int j=1;j<=n;j++)
if (i!=j) ma+=a[j]*t[j][i];
if (ans>ma) {
minn=i; ans=ma;
}
}
printf("%d\n",minn);
printf("%d",ans);
}
代码(100分)
#include<cstdio>
#include<algorithm>
using namespace std;
struct node{
int y,z,next;
}a[300011];
int n,len,last[100011],w;
long long size[100011],sum[100011],q;
void add(int xx,int yy,int zz)
{ a[++len].y=yy; a[len].z=zz; a[len].next=last[xx]; last[xx]=len; }
void dp(int g,int dep,int father)
{
size[g]=sum[g]*dep;//dep为到根节点的距离
for (int i=last[g];i;i=a[i].next)
if (a[i].y!=father)
{
dp(a[i].y,dep+a[i].z,g);
sum[g]+=sum[a[i].y]; //人数
size[g]+=size[a[i].y]; //目前节点路径总和
}
}
void sdp(int g,int father)
{
for (int i=last[g];i;i=a[i].next)
if (a[i].y!=father)
{
size[a[i].y]=size[g]+(sum[1]-2*sum[a[i].y])*a[i].z; //换根
if (size[a[i].y]<q)
{
q=size[a[i].y];
w=a[i].y;
}
sdp(a[i].y,g);
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&sum[i]);
int xx,yy,zz;
for (int i=1;i<n;i++)
{
scanf("%d%d%d",&xx,&yy,&zz);
add(xx,yy,zz); add(yy,xx,zz);
}
q=1e18;w=-1;
dp(1,0,0);
sdp(1,0);
if (size[1]<q)//落下的判断
{
q=size[1];
w=1;
}
printf("%d\n%lld",w,q);
}