【jzoj 1497】 景点中心 {树型动态规划}

题目

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); 
}

猜你喜欢

转载自blog.csdn.net/qq_39897867/article/details/80963690
今日推荐