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

浙公网安备 33010602011771号