Atcoder CODE FESTIVAL 2016 Grand Final E - Water Distribution
阅读原文时间:2023年07月09日阅读:1

Atcoder CODE FESTIVAL 2016 Grand Final E - Water Distribution

题目链接:https://atcoder.jp/contests/cf16-exhibition-final/tasks/cf16_exhibition_final_e

洛谷链接:https://www.luogu.com.cn/problem/AT2230

模拟赛出了这道题,赛时觉得这题是最简单的一题,想了很久但是第一个结论就想错了,知道是状压但始终设不出来状态。

我们思考答案是怎样得到的。显然由一些连通块组成,那么思路就是分别考虑每个连通块的最优解,然后\(O(3^n)\)枚举子集状压合并答案。

想到这一步之后就很简单了。对于一个连通块,我们可以求出其最小生成树,假设最小生成树的各边权之和为\(E\),连通块内水的总量为\(A\),连通块的点数为\(V\),那么这个连通块的答案就是\(\frac{A-E}{V}\)。

连通块的最小生成树可以\(O(2^n n^2)\text{DP}\)求出。于是总复杂度为\(O(2^n n^2 + 3^n)\)。

点我看代码

#include <cstdio>
#include <iostream>
#include <cmath>
#define db double
using namespace std;
inline void read(int &x) {
    x = 0; int f = 0; char ch = getchar();
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48);
    if(f) x = ~x + 1;
}
const int N = 16;
const db inf = 1e18;
int n;
struct City {
    db x, y, a;
}p[N];
db dis[N][N];
inline db sqr(db x) {return x * x;}
db f[1 << N], g[1 << N];
inline db popcount(int x) {int res = 0; while(x) ++res, x &= x - 1; return res;}
inline void update(db &x, db y) {x = min(x, y);}
int main() {
    freopen("water.in","r",stdin);
    freopen("water.out","w",stdout);
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i) scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].a);
    for(int i = 1; i <= n; ++i)
        for(int j = i + 1; j <= n; ++j)
            dis[i][j] = dis[j][i] = sqrt(sqr(p[i].x - p[j].x) + sqr(p[i].y - p[j].y));
    for(int i = 0; i < 1 << n; ++i) f[i] = inf;
    for(int i = 1; i <= n; ++i) f[1 << i - 1] = 0;
    for(int s = 1; s < 1 << n; ++s)
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= n; ++j)
                if((s & (1 << i - 1)) && !(s & (1 << j - 1))) update(f[s | (1 << j - 1)], f[s] + dis[i][j]);
    for(int s = 1; s < 1 << n; ++s) {
        for(int i = 1; i <= n; ++i) if(s & (1 << i - 1)) g[s] += p[i].a;
        g[s] -= f[s], g[s] /= popcount(s);
        for(int t = (s - 1) & s; t; t = (t - 1) & s)
            g[s] = max(g[s], min(g[t], g[s ^ t]));
    }
    printf("%.12lf\n",g[(1 << n) - 1]);
}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章