[AT2230] [Code Festival 2016 Grand Final -E] Water Distribution

本文探讨了一个涉及多个城市的水资源分配问题,目标是最小化各城市间水资源的差异。通过构建子图,利用最小生成树算法,并结合动态规划,提出了一个复杂度为O(2^n*n^2*log(n)+3^n)的解决方案。
洛谷传送门
Atcoder传送门

题目描述

在一个二维平面上有 N N N个城市, 第 i i i个城市的坐标是 ( x i , y i ) (x_i,y_i) (xi,yi), 一开始拥有的水量是 a i a_i ai

现在你可以从一个城市向另一个城市运送任意数量的水, 但水在运输过程中会有损耗, 具体而言如果从 x x x城市运 l l l水到 y y y城市,最终 y y y城市得到的水量是 m a x ( 0 , l − d i s ( x , y ) ) max(0,l-dis(x,y)) max(0,ldis(x,y)), 其中 d i s ( x , y ) dis(x,y) dis(x,y) x x x y y y城市间的欧几里得距离。 你可以多次进行这个操作。

你要使最终水量最少的城市水量尽量多, 求这个值。

输入输出格式

输入格式

第一行一个正整数 N N N
以下 N N N行, 每行三个整数 x i , y i , a i x_i,y_i,a_i xi,yi,ai, 含义如上。

输出格式

一行一个实数 a n s ans ans表示最终水量最少的城市水量最多有多少。

输入输出样例

输入样例#1:
3
0 0 10
2 0 5
0 5 8
输出样例#1:
6.500000000000
输入样例#2:
15
335279264 849598327 822889311
446755913 526239859 548830120
181424399 715477619 342858071
625711486 448565595 480845266
647639160 467825612 449656269
160714711 336869678 545923679
61020590 573085537 816372580
626006012 389312924 135599877
547865075 511429216 605997004
561330066 539239436 921749002
650693494 63219754 786119025
849028504 632532642 655702582
285323416 611583586 211428413
990607689 590857173 393671555
560686330 679513171 501983447
输出样例#2:
434666178.237122833729

解题分析

很显然我们选取任意一个子图出来, 能够取到的最大答案要么是这个子图的子图合并起来的最小值, 要么是这个子图的所有点的权值之和减去最小生成树上所有边的长度(如果出现运到某个城市的水是负数也不碍事, 肯定这样是不优的, 我们讨论子集的时候已经考虑到了)。

所以枚举所有子图做最小生成树, 然后枚举子集大力 D P DP DP就好了。

总复杂度 O ( 2 n n 2 l o g ( n ) + 3 n ) O(2^nn^2log(n)+3^n) O(2nn2log(n)+3n)

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <vector>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define db long double
#define ll long long
#define MX 16
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int n, all, cnt;
int bel[MX];
std::vector <int> st;
struct INFO {int x, y, val;} dat[MX];
struct Edge {int from, to; db len;} edge[MX * MX];
IN bool operator < (const Edge &x, const Edge &y)
{return x.len < y.len;}
db res[1 << MX];
IN ll sqr(R int x) {return 1ll * x * x;}
IN db calc(R int x, R int y)
{return sqrtl(sqr(dat[x].x - dat[y].x) + sqr(dat[x].y - dat[y].y));}
int find(R int now) {return bel[now] == now ? now : bel[now] = find(bel[now]);}
IN db MST(R int stat)
{
	st.clear(); cnt = 0;
	db ret = 0;
	for (R int i = 0; i < n; ++i)
	{
		bel[i] = i;
		if ((1 << i) & stat) st.push_back(i), ret += dat[i].val;
	}
	int tot = st.size(), foo, bar, cot = 0;
	for (R int i = 0; i < tot; ++i)
	for (R int j = i + 1; j < tot; ++j)
	edge[++cnt] = {st[i], st[j], calc(st[i], st[j])};
	std::sort(edge + 1, edge + 1 + cnt);
	for (R int i = 1; i <= cnt; ++i)
	{
		foo = find(edge[i].from), bar = find(edge[i].to);
		if (foo == bar) continue;
		++cot;
		bel[foo] = bar;
		ret -= edge[i].len;
		if (cot == tot - 1) break;
	}
	return ret / tot;
}
int main(void)
{
	in(n); all = (1 << n) - 1;
	for (R int i = 0; i < n; ++i)
	in(dat[i].x), in(dat[i].y), in(dat[i].val);
	for (R int i = 1; i <= all; ++i)
	{
		res[i] = MST(i);
		for (R int s = (i - 1) & i; s; s = (s - 1) & i)
		res[i] = std::max(res[i], std::min(res[s], res[i ^ s]));
	}
	printf("%.10Lf", res[all]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值