Loading

题解:AT_cf16_exhibition_final_e Water Distribution

题目链接:link

这道题目我们有 \(3\) 个结论:

  1. 在最优情况下,最后所有的点上的水量都是一样的。因为水多的可以向水少的运水。
  2. 不存在间接运水的情况,这个由三角形的三边关系可以得到。
  3. 最优运输路径,最后是树形的。

我们在不漏情况的条件下,枚举所有的树形。

接下来就是动态规划了!

很明显这道题目需要使用状压 dp。

我们让 \(dp_{i}\) 表示在子集 \(i\) 状态下的最小值的最大值。

而我们的动态规划的答案是 \(dp_{2^n-1}\)

对于每个子集 \(i\) 我们做整体和分割处理,整体处理就是把子集里的所有城市看成一个连通体,如果我们设总水量为 \(S\) 城市数量为 \(M\) 那么最小值最大化就是 \(\frac{S-MST}{M}\) 其中 \(MST\) 代表这个子集的最小生成树。

然后分割处理就是将子集 \(i\) 分成两个不相交的子集 \(j\)\(i\oplus j\) 我们分别计算 \(dp_j\)\(dp_{i\oplus j}\) 然后与其他方式进行比较就好了。

上代码!

#include<bits/stdc++.h>
#define I using
#define AK namespace
#define IOI std
#define i_ak return
#define ioi  0
#define i_will signed
#define ak main
#define IMO ()
#define int long long
I AK IOI;
const long double inf=1e18;
int a[20],b[20],c[20],n;
long double d[20][20],t[1<<16],dp[1<<16]; 
int counter(int x){
    int ans=0;
    while(x){
        ans+=x&1;
        x/=2;
    }
    return ans;
}
i_will ak IMO{
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i]>>b[i]>>c[i];
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)d[i][j]=sqrt(1ll*(a[i]-a[j])*(a[i]-a[j])+1ll*(b[i]-b[j])*(b[i]-b[j]));
    for(int i=0;i<(1<<n);i++)t[i]=inf;
    for(int i=1;i<=n;i++)t[1<<(i-1)]=0;
    for(int i=1;i<(1<<n);i++)for(int j=1;j<=n;j++)if(i>>(j-1)&1)for(int k=1;k<=n;k++)if(!(i>>(k-1)&1))t[i|(1<<(k-1))]=min(t[i|(1<<(k-1))],t[i]+d[j][k]);
    for(int i=1;i<(1<<n);i++){
        long double w=0;
        for(int j=1;j<=n;j++)if(i>>(j-1)&1)w+=c[j];
        if(w>t[i])dp[i]=(w-t[i])/counter(i); 
        for(int t=i&(i-1);t!=0;t=(t-1)&i)dp[i]=max(dp[i],min(dp[t],dp[i^t]));
    }
    printf("%.10Lf\n",dp[(1<<n)-1]); 
    i_ak ioi;
}

亲测可过,请勿抄袭!

posted @ 2025-06-09 15:40  盼满天繁星  阅读(14)  评论(0)    收藏  举报