【GDKOI2014】石油储备计划

Description

在这里插入图片描述

Data Constraint

Data Constraint
对于20%的数据,N<=15

对于100%的数据,T<=10,N<=100,0<=si<=10000,1<=X,Y<=N,1<=Z<=10000。

Solution

经过观察可以发现,每一种平衡状态一定是由sum个x和sum1个x+1组成的。
其中 s u m 1 = ∑ a i n sum1=\frac{\sum a_{i}}{n} sum1=nai
s u m = n − s u m 1 sum=n-sum1 sum=nsum1
可以考虑树型dp。
f [ i ] [ j ] f[i][j] f[i][j]表示以 i i i号节点为根的子树油桶数为 x + 1 x+1 x+1的节点数为 j j j的最小代价
那么状态转移方程为:
f [ i ] [ j ] = m i n ( f [ i ] [ j − k ] + f [ s o n ] [ k ] + a b s ( s [ i ] − x ∗ ( s i z e [ s o n ] − j ) − ( x + 1 ) ∗ j ) ∗ l e n ) f[i][j]=min(f[i][j-k]+f[son][k]+abs(s[i]-x*(size[son]-j)-(x+1)*j)*len) f[i][j]=min(f[i][jk]+f[son][k]+abs(s[i]x(size[son]j)(x+1)j)len)
其中 s [ i ] s[i] s[i]为以i为根的子树中总的初始油桶数量, s i z e [ s o n ] size[son] size[son]表示以 s o n son son为根的子树的节点数。这句话的意思就是把儿子多出来的运到父亲这,如果儿子少了,就是从父亲这运过来,所以要取绝对值。
这道题还可以用费用流做,只不过我太弱,不会做。

Code

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
int T,n,cnt,sum,val,val1,Size;
int a[101],head[101],size[101];
ll s[101],f[101][101],g[101];
struct node {
	int to,len,next;
}edge[201];
inline int read() {
	int s=0;
	char ch=getchar();
	while(ch<48||ch>57)ch=getchar();
	while(ch>=48&&ch<=57)
		s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return s;
}
inline void add(int x,int y,int z) {
	edge[cnt]=(node){y,z,head[x]};
	head[x]=cnt++;
}
inline void dfs(int now,int fa) {
	size[now]=1;s[now]=a[now];
	f[now][0]=f[now][1]=0;
	for(int i=head[now];i!=-1;i=edge[i].next) {
		int son=edge[i].to;
		if(son==fa)continue;
		dfs(son,now);
	}
	for(int i=head[now];i!=-1;i=edge[i].next) {
		int son=edge[i].to,len=edge[i].len;
		if(son==fa)continue;
		size[now]+=size[son];
		s[now]+=s[son];
		memset(g,0x7f7f7f7f,sizeof(g));
		for(int j=0;j<=Size&&j<=size[now];++j)
			for(int k=0;k<=size[son]&&k<=j;++k)
				g[j]=min(g[j],f[now][j-k]+f[son][k]+abs(s[son]-(size[son]-k)*val-k*(val+1))*len);
		//不能直接转移,要把所有答案存下来再转移过去。不然会重复
		for(int j=0;j<=Size&&j<=size[now];j++)f[now][j]=g[j];
	}
}
int main() {
	T=read();
	while(T--) {
		n=read();cnt=0;sum=0;
		for(int i=1;i<=n;++i)
			a[i]=read(),sum+=a[i],head[i]=-1;
		Size=sum%n;val=sum/n;
		for(int i=1;i<n;++i) {
			int u=read(),v=read(),w=read();
			add(u,v,w);add(v,u,w);
		}
		memset(f,0x7f7f7f7f,sizeof(f));
		dfs(1,0);
		printf("%lld\n",f[1][Size]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值