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