Description
Input
Output
对于每组数据,输出一个整数,表示达到“平衡”状态所需的最小代价。
Sample Input
2 3 6 1 5 1 2 1 2 3 2 5 4 5 4 3 2 1 3 1 1 2 2 2 4 3 2 5 4
Sample Output
4 4 样例解释: 对于第一组数据,从城市1到城市2运输2桶石油,代价为1*2=2;从城市3往城市2运输1桶石油,代价为2*1=2。此时三个城市储备量都为4桶,该状态的平衡度为0。 对于第二组数据,从城市2到城市5运输1桶石油,代价为1*4=4;此时五个城市储备量为(4,4,4,3,3),该状态的非平衡度为1.2,是能达到的所有状态的最小值。
Data Constraint
对于20%的数据,N<=15
对于100%的数据,T<=10,N<=100,0<=si<=10000,1<=X,Y<=N,1<=Z<=10000。
Solution
树形dp。
设sum为石油总和。
对于sum%n=0的情况,每一个点的最终石油量是一样的,为sum/n,那么计算出每一个子树内经过当前边的对答案的贡献即可。
时间O(n)
对于sum%n!=0的情况,两个ave1=sum/n,ave2=sum/n+1。
设f[ i ][ j ]表示以i为根的子树中,有j个值为sum/n+1的点,其余size[ i ]-j个点为sum/n的最优答案。
先枚举儿子。
设g[ ]表示之前处理出的所有儿子的答案。初值g[ 0 ]=g[ 1 ]=0(g[ 1 ]表示当前点x为sum/n+1的点的答案为0)
考虑枚举儿子节点中有k个sum/n+1的点,
那么引入临时中间转移数组F[],F[ j ]=f[ son ][ k ]+g[ j-k ]+cost。
其中cost为abs( (sum/n+1)的点值*k个+(sum/n)的值*(size [ son ] -k)个 - sumoil[ son ]子树内现有的石油量 )差值表示需要运进来或运出去的代价* len当前边的长度。
最后再把g赋值成F实现更新即可。
最后答案为f[ 1 ][ sum%n ]。即有sum%n个sum/n+1的点。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define I int
#define ll long long
#define son t[k]
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof a)
#define N 105
using namespace std;
I T,n,a[N],x,y,z,s[N],t[N<<1],nx[N<<1],w[N<<1],ls[N],tot;
ll f[N][N],g[N],F[N],sz[N],sum;
void add(I x,I y,I z){t[++tot]=y,nx[tot]=ls[x],ls[x]=tot,w[tot]=z;}
void dg(I x,I y){
sz[x]=a[x];
for(I k=ls[x];k;k=nx[k]) if(son!=y){
dg(son,x);
sz[x]+=sz[son];
f[1][0]+=w[k]*abs(sz[son]-sum/n);
}
}
void dp(I x,I y){
s[x]=1,sz[x]=a[x];
for(I k=ls[x];k;k=nx[k]) if(son!=y){
dp(son,x);
sz[x]+=sz[son],s[x]+=s[son];
}
mem(g,60);
g[0]=g[1]=0;
for(I k=ls[x];k;k=nx[k]) if(son!=y){
mem(F,60);
F(i,0,min((ll)s[x],sum%n)){
F(j,0,min(i,s[son])){
F[i]=min(F[i],f[son][j]+g[i-j]+abs((sum/n+1)*j+sum/n*(s[son]-j)-sz[son])*w[k]);
}
}
F(i,0,min((ll)s[x],sum%n)) g[i]=F[i];
}
F(i,0,min((ll)s[x],sum%n)) f[x][i]=g[i];
}
I main(){
freopen("plan.in","r",stdin);
freopen("plan.out","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%d",&n);
mem(nx,0),mem(ls,0),mem(sz,tot=sum=0);
F(i,1,n) scanf("%d",&a[i]),sum+=a[i];
F(i,1,n-1){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z),add(y,x,z);
}
// if(sum%n==0) f[1][0]=0,dg(1,0);
dp(1,0);
printf("%lld\n",f[1][sum%n]);
}
return 0;
}