【AT2230】Water Distribution

【AT2230】Water Distribution

by AmanoKumiko

Description

平面上有\(n\)座城市,第\(i\)城市有坐标\((x_i,y_i)\)并有初始储水量\(a_i\)。 水可以在城市之间运输,但运输过程会有损耗。具体地,如果计划从城市\(i\)向城市\(j\)运水\(x\), 则城市\(i\)水量减少\(x\),城市\(j\)增加的水量\(y = max(x−dis_{i,j},0)\)\(dis_{i,j}\) 表示两城市之间的欧几里得距离。单次运水量\(x\)应小于等于城市\(i\)原有储水量。 试通过若干次运水(不限次数)使所有城市水量最小值最大。

Input

第一行一个整数\(n\)

然后\(n\)行每行三个整数\(x,y,a\)

Output

一行一个小数表示答案

Sample Input

3
0 0 10
2 0 5
0 5 8

Sample Output

6.500000000000

Data Constraint

\(1\le n\le 15\)

Solution

猜错结论啦

我们给输水关系连边

那么一定不会有环,否则考虑每条边输的水减去环上最小值,一定更优

那么也就是形成了树

考虑这个树的上界,设\(a\)的总和为\(G\),边权和\(A\),点数为\(K\),即\(M=\frac{G-A}{K}\)

我们随便选一个做根,然后分析点\(x\),父亲\(y\)

如果\(a_x\ge M\),那么可以将\(a_x-M\)转移到\(y\)

如果\(a_x\le M\),那么我们将\(x\)视为根,从\(y\)转移就行了

所以上界是能达到的

接下来要做的就是枚举集合,\(MST\),最后用\(dp\)拼起来

Code

#include<bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define Ld long double
#define N 20

int n,fa[N],tot;
Ld dis[N][N],f[1<<N],S[1<<N];
struct node{Ld x,y,a;}a[N];
struct mode{int st,en;Ld val;}e[N*N];

int get(int x){return fa[x]==x?x:(fa[x]=get(fa[x]));}

Ld sqr(Ld x){return x*x;}

bool cmp(mode x,mode y){return x.val<y.val;}

int main(){
	scanf("%d",&n);
	F(i,1,n)scanf("%Lf%Lf%Lf",&a[i].x,&a[i].y,&a[i].a);
	F(i,1,n) F(j,1,n)dis[i][j]=sqrt(sqr(a[i].x-a[j].x)+sqr(a[i].y-a[j].y));
	F(i,1,(1<<n)-1){
		F(j,1,n)fa[j]=j;
		tot=0;
		F(j,1,n) F(k,1,n)if(((1<<j-1)&i)&&((1<<k-1)&i))e[++tot]=(mode){j,k,dis[j][k]};
		sort(e+1,e+tot+1,cmp);
		int cnt=0;
		F(j,1,n)if((1<<j-1)&i)f[i]+=a[j].a,cnt++;
		F(j,1,tot){
			int A=get(e[j].st),B=get(e[j].en);
			if(A==B)continue;
			fa[A]=B;
			f[i]-=e[j].val;
		}
		f[i]/=cnt;
	}
	F(i,0,(1<<n)-1){
		for(int j=i;j;j=i&(j-1))
			f[i]=max(f[i],min(f[j],f[i^j]));
	}
	printf("%.13Lf",f[(1<<n)-1]);
	return 0;
}
posted @ 2022-07-17 22:20  冰雾  阅读(34)  评论(0编辑  收藏  举报