Water Distribution

\(\text{Problem}:\)Water Distribution

\(\text{Solution}:\)

  • \(F(S)\) 表示点集 \(S\) 的最优解。考虑存在四个点 \(a,b,c,d\),并需要做 \(a\rightarrow b\)\(c\rightarrow d\) 的操作,那么 \(a,b\)\(c,d\) 之间是独立的,也就是说 \(F(\{a,b,c,d\})=\min\{F(\{a,b\}),F(\{c,d\})\}\)

  • 考虑有两个操作 \(i\rightarrow j\)\(i\rightarrow k\),则可以比较 \(i\rightarrow j \rightarrow k\) 和分别做 \(i\rightarrow j\)\(i\rightarrow k\) 取总路径最短的方案。

这启发我们可以把需要操作的两个点 \(i,j\) 放在一个连通块中。那么问题转化为对每个连通块求最优解。

对于一个连通块,设初始总水量和为 \(W\),总操作路径长度为 \(B\),总点数为 \(K\),则最优解上界为 \(G=\cfrac{W-B}{K}\)。首先,使得 \(B\) 最小。显然,对连通块建出完全图后,求出最小生成树即可。接下来考虑对于任意情况都可以使最优解达到上界。随便钦定一个节点为最小生成树根,那么叶子节点 \(x\) 与其父亲节点 \(y\) 有以下关系:

  • \(a_{x}\geq G\),将 \(a_{x}-G\) 转移到 \(y\) 节点即可。
  • \(a_{x}<G\),此时把 \(x\) 看作根节点,可以将 \(G-a_{x}\) 转移到 \(y\),然后转移给 \(x\) 即可。

故一个连通块的答案就是 \(G\)。那么对于所有点集,预处理出点集作为一个连通块的答案,这一部分的时间复杂度为 \(O(2^{n}n^{2}\log n)\)。然后枚举子集 \(dp\),这一部分的时间复杂度为 \(O(3^{n})\)

\(\text{Code}:\)

#include <bits/stdc++.h>
#pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
using namespace std; const int N=1010;
inline int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
}
int n;
int X[N],Y[N],a[N],p[N];
double F[(1<<16)+5],W[(1<<16)+5],B[(1<<16)+5],dp[(1<<16)+5];
struct Node { int u,v; double w; }e[N]; int tot;
inline double Dis(int i,int j)
{
	return sqrt(1.0*(X[i]-X[j])*(X[i]-X[j])+1.0*(Y[i]-Y[j])*(Y[i]-Y[j]));
}
inline bool cp(Node x,Node y) { return x.w<y.w; }
struct Uni
{
	int f[N];
	inline void Init()
	{
		for(ri int i=1;i<=n;i++) f[i]=i;
	}
	inline int Find(int x) { return f[x]^x?f[x]=Find(f[x]):x; }
}A;
signed main()
{
	n=read();
	for(ri int i=1;i<=n;i++) X[i]=read(), Y[i]=read(), a[i]=read();
	for(ri int i=1;i<(1ll<<n);i++)
	{
		int cnt=0;
		for(ri int j=1;j<=n;j++) if((i>>(j-1))&1) p[++cnt]=j, W[i]+=1.0*a[j];
		if(cnt==1)
		{
			F[i]=W[i];
			continue;
		}
		tot=0;
		for(ri int j=1;j<=cnt;j++)
		for(ri int k=j+1;k<=cnt;k++)
		e[++tot]=(Node){p[j],p[k],Dis(p[j],p[k])};
		sort(e+1,e+1+tot,cp);
		A.Init();
		for(ri int j=1;j<=tot;j++)
		{
			int fx=A.Find(e[j].u), fy=A.Find(e[j].v);
			if(fx==fy) continue;
			A.f[fx]=fy, B[i]+=e[j].w;
		}
		F[i]=max(0.0,(W[i]-B[i])/cnt);
	}
	dp[0]=1e18;
	for(ri int i=1;i<(1ll<<n);i++)
	{
		for(ri int j=i;j;j=i&(j-1))
		{
			int k=i^j;
			dp[i]=max(dp[i],min(F[j],dp[k]));
		}
	}
	printf("%.12lf\n",dp[(1ll<<n)-1]);
	return 0;
}
posted @ 2021-03-02 18:37  zkdxl  阅读(59)  评论(0编辑  收藏  举报