• 【bzoj1827】[Usaco2010 Mar]gather 奶牛大集会


    题目描述

             Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会。当然,她会选择最方便的地点来举办这次集会。每个奶牛居住在 N(1<=N<=100,000) 个农场中的一个,这些农场由N-1条道路连接,并且从任意一个农场都能够到达另外一个农场。道路i连接农场A_i和B_i(1 <= A_i <=N; 1 <= B_i <= N),长度为L_i(1 <= L_i <= 1,000)。集会可以在N个农场中的任意一个举行。另外,每个牛棚中居住着C_i(0 <= C_i <= 1,000)只奶牛。在选择集会的地点的时候,Bessie希望最大化方便的程度(也就是最小化不方便程度)。比如选择第X个农场作为集会地点,它的不方便程度是其它牛棚中每只奶牛去参加集会所走的路程之和,(比如,农场i到达农场X的距离是20,那么总路程就是C_i*20)。帮助Bessie找出最方便的地点来举行大集会。  

    输入

    第一行:一个整数N

    第二到N+1行:第i+1行有一个整数C_i

    第N+2行到2*N行,第i+N+1行为3个整数:A_i,B_i和L_i。

    输出

    一个数字表示答案

    样例

    Input

    Output

    5
    1
    1
    0
    0
    2
    1 3 1
    2 3 2
    3 4 3
    4 5 3

    15

    对于

    20%数据n<20

    50%数据 n<2000

    100%数据n<100,000

    首先想到的就是用链表。。

    然后想到将每个点为终点的答案算出来。在算的时候发现找终点这一过程是O(n²)

    先附上50%AC暴力代码:

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<queue>
    using namespace std;
    queue<int>q;
    int dis[2005][2005];
    int c[4005];
    int head[4005],next[4005],tov[4005];
    bool b[4005];
    int tot;
    long long minx=5000000000LL,ans;
    int n;
    void go(int x,int y,int z)
    {
      tot++;
      tov[tot]=y;
      next[tot]=head[x];
      head[x]=tot;
    }
    int main()
    {
      memset(dis,-1,sizeof(dis));
      freopen("A.in","r",stdin);
      freopen("A.out","w",stdout);
      scanf("%d",&n);//cin>>n;
      for(int i=1;i<=n;i++)
      scanf("%d",&c[i]);//cin>>c[i];
      for(int i=1;i<n;i++)
      {
        int x,y,z;
        scanf("%d %d %d",&x,&y,&z);//cin>>x>>y>>z;
        dis[x][y]=dis[y][x]=z;
        go(x,y,z);
        go(y,x,z);
      }
        for(int i=1;i<=n;i++)
        {
          memset(b,false,sizeof(b));
          q.push(i);
          while(!q.empty())
          {
            int u=q.front();
            b[u]=true;
            q.pop();
            int v=head[u];
            while(v)
            {
              if(i==u)
              q.push(tov[v]);
              else if(i!=u&&b[tov[v]]==false)
              {
                q.push(tov[v]);
                dis[i][tov[v]]=dis[tov[v]][i]=dis[i][u]+dis[u][tov[v]];
                b[tov[v]]=true;
              }
                v=next[v];
            }
          }
      }
        for(int i=1;i<=n;i++)
        {
          ans=0;
          for(int j=1;j<=n;j++)
          {
            if(i!=j&&c[j]!=0)
            ans+=dis[i][j]*c[j];
          }
            if(ans<minx)minx=ans;
        }
          printf("%d",minx);//cout<<minx;
          exit(0);
          return 0;
    }

    后来发现2遍dfs即可

    输入时建立链表,第一次dfs找其它点奶牛到1号点总共的花费(用sum[i]逐加实现),第二次dfs找其它点,

    方程为ans[x]=ans[fa]-num[x]*cost+(SUM-num[x])*cost;意思是减去当前节点到父亲的花费,再加上其它点多走的花费。

    代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    int head[100005];
    int next[200005],tov[200005],w[200005];
    int tot;long long SUM;
    int c[100005];
    int n;
    long long ans[100005];
    long long sum[100005],num[100005];
    long long minx=10000000000000LL;
    void go(int x,int y,int z)
    {
      tov[++tot]=y;
      next[tot]=head[x];
      head[x]=tot;
      w[tot]=z;
    }
    void dp1(int x,int fa)
    {
      num[x]=c[x];
      sum[x]=0;
      for(int i=head[x];i;i=next[i])
      {
        if(tov[i]==fa) continue;
        dp1(tov[i],x);
        num[x]+=num[tov[i]];
        sum[x]+=sum[tov[i]]+num[tov[i]]*w[i];
      }
    }
    void dp2(int x,int fa,int cost)
    {
      if(!fa)
      ans[x]=sum[x];//第一次
      else ans[x]=ans[fa]-num[x]*cost+(SUM-num[x])*cost;//倒
      for(int i=head[x];i;i=next[i])
      {
        if(fa==tov[i]) continue;
        dp2(tov[i],x,w[i]);
      }
    }
    int main()
    {
    ios::sync_with_stdio(false);
    freopen("A.in","r",stdin);
    freopen("A.out","w",stdout);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
      cin>>c[i];
      SUM+=c[i];
    }
    for(int i=1;i<n;i++)
    {
      int x,y,z;
      cin>>x>>y>>z;
      go(x,y,z);
      go(y,x,z);
    }
      dp1(1,0);//算出ans[1]
      dp2(1,0,0);
      for(int i=1;i<=n;i++)
      {
        if(minx>ans[i])
        minx=ans[i];
      }
        cout<<minx;
        return 0;
    }

  • 相关阅读:
    计算机二进制总结
    java-集合排序,队列,散列表map以及如何遍历
    java-Collection,List简单使用与方法/(集合使用-中)
    java-Date类与集合(上)
    java-正则、object中的两个方法的使用
    java-注释、API之字符串(String)
    Java-面向对象三大特征、设计规则
    java-多态、内部类
    java-修饰词、抽象类、抽象方法
    java-重载、包修饰词以及堆栈管理
  • 原文地址:https://www.cnblogs.com/937337156Zhang/p/5812384.html
Copyright © 2020-2023  润新知