2015编程之美资格赛 C 基站选址



时间限制:2000ms

单点时限:1000ms

内存限制:256MB

描述

需要在一个N × M的网格中建立一个通讯基站,通讯基站仅必须建立在格点上。

网格中有A个用户,每个用户的通讯代价是用户到基站欧几里得距离的平方。

网格中还有B个通讯公司,维护基站的代价是基站到最近的一个通讯公司的路程(路程定义为曼哈顿距离)。

在网格中建立基站的总代价是用户通讯代价的总和加上维护基站的代价,最小总代价。

输入

第一行为一个整数T,表示数据组数。

每组数据第一行为四个整数:N, M, A, B。

接下来的A+B行每行两个整数x, y,代表一个坐标,前A行表示各用户的坐标,后B行表示各通讯公司的坐标。

输出

对于每组数据输出一行"Case #X: Y",X代表数据编号(从1开始),Y代表所求最小代价。

数据范围

1 ≤ T ≤ 20

1 ≤ x ≤ N

1 ≤ y ≤ M

1 ≤ B ≤ 100

小数据

1 ≤ N, M ≤ 100

1 ≤ A ≤ 100

大数据

1 ≤ N, M ≤ 107

1 ≤ A ≤ 1000

样例输入
2
3 3 4 1
1 2
2 1
2 3
3 2
2 2
4 4 4 2
1 2
2 4
3 1
4 3
1 4
1 3
样例输出
Case #1: 4
Case #2: 13

2.解题思路:本题要求找一个合适的位置建立一个基站,使得总费用最小。根据题意描述,费用包含两个部分,第一部分是用户到基站的欧几里得距离的平方,第二个是到最近的通讯公司的曼哈顿距离。不难想象,这个基站的位置应该大致处于n个用户中间的区域。其实,可以通过列式推导出最合适的位置的大致区域。

首先考虑用户的距离之和d1=sum{(x-xi)^2}+sum{(y-yi)^2},如果距离达到最小,那么该点一定是一个极小值点。根据高等数学的知识,该处的偏导数都为0,。因此可以事先对x,y求偏导。发现,x=sum{xi}/n,y=sum{yi}/n,因此可以对该点以及周围的8个点进行搜索。那么曼哈顿距离此时是否为最小呢?不难发现,曼哈顿距离的改变量每移动一格只会变化最多一个距离,但欧几里得距离最大的变化量会大于1个距离单位,因此应该尽量保证d1达到最小。

因此,只需要在平均值以及其周围的8个点中找出总距离最小的,就是最终的结果了。

3.代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;

#define mxn 200005
#define LL long long
#define MP make_pair
#define REP(i, a, b) for (int i = a; i <= b; ++i)
#define FOR(i, a, b) for (int i = a; i < b; ++i)

int dx[] = { 0, 0, 0, 1, 1, 1, -1, -1, -1 };
int dy[] = { 0, 1, -1, 0, 1, -1, 0, 1, -1 };

LL ABS(LL x)
{
	return x < 0 ? -x : x;
}

struct point
{
	LL x, y;//考虑到大数据范围,选用long long
	point(){};
	point(LL x, LL y) :x(x), y(y){}
	point operator - (const point& b) const //重载减号,方便输入
	{
		return point(x - b.x, y - b.y);
	}
	void input()
	{
		cin >> x >> y;
	}
	LL dis() //欧几里得距离的平方
	{
		return x * x + y * y;
	}
	LL len() //曼哈顿距离
	{
		return ABS(x) + ABS(y);
	}
}A[1005], B[105];

LL cal(point o, int a, int b) //计算(a,b)点出的总距离
{
	LL ret = ~0uLL >> 1;
	REP(i, 1, b) ret = min(ret, (o - B[i]).len());
	REP(i, 1, a) ret += (o - A[i]).dis();
	return ret;
}

int main()
{
	//freopen("t.txt", "r", stdin);
	int t, n, m, a, b, cas = 0;
	cin >> t;
	while (t--)
	{
		cin >> n >> m >> a >> b;
		REP(i, 1, a) A[i].input();
		REP(i, 1, b) B[i].input();
		LL ans = ~0uLL >> 1, x = 0, y = 0;
		REP(i, 1, a) x += A[i].x, y += A[i].y;
		x /= a, y /= a;//求平均值
		FOR(k, 0, 9) //在平均值及其附近的8个点中找到最小距离
		{
			LL tx = x + dx[k], ty = y + dy[k];
			if (tx < 1 || tx > n || ty < 1 || ty > m) continue;//出界
			ans = min(ans, cal(point(tx, ty), a, b));
		}
		cout << "Case #" << ++cas << ": " << ans << endl;
	}
	return 0;
}
时间: 2024-10-17 09:24:11

2015编程之美资格赛 C 基站选址的相关文章

[2015编程之美] 资格赛C

#1150 : 基站选址 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 需要在一个N × M的网格中建立一个通讯基站,通讯基站仅必须建立在格点上. 网格中有A个用户,每个用户的通讯代价是用户到基站欧几里得距离的平方. 网格中还有B个通讯公司,维护基站的代价是基站到最近的一个通讯公司的路程(路程定义为曼哈顿距离). 在网格中建立基站的总代价是用户通讯代价的总和加上维护基站的代价,最小总代价. 输入 第一行为一个整数T,表示数据组数. 每组数据第一行为四个整数:N, M

2015编程之美资格赛 回文子序列个数

时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 给定字符串,求它的回文子序列个数.回文子序列反转字符顺序后仍然与原序列相同.例如字符串aba中,回文子序列为”a”, “a”, “aa”, “b”, “aba”,共5个.内容相同位置不同的子序列算不同的子序列. 输入 第一行一个整数T,表示数据组数.之后是T组数据,每组数据为一行字符串. 输出 对于每组数据输出一行,格式为”Case #X: Y”,X代表数据编号(从1开始),Y为答案.答案对100007取模. 数据范围 

2015编程之美资格赛 A 2月29日

 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 给定两个日期,计算这两个日期之间有多少个2月29日(包括起始日期). 只有闰年有2月29日,满足以下一个条件的年份为闰年: 1. 年份能被4整除但不能被100整除 2. 年份能被400整除 输入 第一行为一个整数T,表示数据组数. 之后每组数据包含两行.每一行格式为"month day, year",表示一个日期.month为{"January", "February&quo

2015编程之美资格赛 B 回文字符序列

 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 给定字符串,求它的回文子序列个数.回文子序列反转字符顺序后仍然与原序列相同.例如字符串aba中,回文子序列为"a", "a", "aa", "b", "aba",共5个.内容相同位置不同的子序列算不同的子序列. 输入 第一行一个整数T,表示数据组数.之后是T组数据,每组数据为一行字符串. 输出 对于每组数据输出一行,格式为&q

编程之美资格赛 大神与三位小伙伴

题目2 : 大神与三位小伙伴 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 L国是一个有着优美景色且物产丰富的国家,很多人都喜欢来这里旅游并且喜欢带走一些纪念品,大神同学也不例外.距离开L国的时间越来越近了,大神同学正在烦恼给她可爱的小伙伴们带什么纪念品好,现在摆在大神同学面前的有三类纪念品A, B, C可以选择,每类纪念品各有N种.其中种类为A_i, B_i, C_i的纪念品价值均为i, 且分别有N+1-i个剩余.现在大神同学希望在三类纪念品中各挑选一件然后赠送给

编程之美2015资格赛:基站选址

题目3 : 基站选址 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 需要在一个N × M的网格中建立一个通讯基站,通讯基站仅必须建立在格点上. 网格中有A个用户,每个用户的通讯代价是用户到基站欧几里得距离的平方. 网格中还有B个通讯公司,维护基站的代价是基站到最近的一个通讯公司的路程(路程定义为曼哈顿距离). 在网格中建立基站的总代价是用户通讯代价的总和加上维护基站的代价,最小总代价. 输入 第一行为一个整数T,表示数据组数. 每组数据第一行为四个整数:N, M,

2015微软编程之美资格赛骨牌覆盖(矩阵快速幂)

由于棋盘只有两行,所以如果第i列的骨牌竖着放,那么就转移为第1列到第i-1列骨牌有多少种摆法 如果第一行第i列骨牌横着放,那么第二行第i列也要横着放,那么就转移为了第1列到第i-2列骨牌有多少种方法 dp[i] = dp[i-1] + dp[i-2],但是列数太多了. 这种递推的算式可以用矩阵快速幂来优化 所以时间复杂度瞬间变为O(logn) 1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h>

[编程之美]资格赛 B Palindrome

既然这个是资格赛,  时间也比较充裕, 我就讲解一下我做题的过程 Time Limit:2000ms Case Time Limit:1000ms Memory Limit:256MB Description Given a string, calculate the number of subsequences that are palindrome. A palindrome is a sequence of characters that reads the same backward o

[2015编程之美] 第一场A

#1156 : 彩色的树 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 给定一棵n个节点的树,节点编号为1, 2, …, n.树中有n - 1条边,任意两个节点间恰好有一条路径.这是一棵彩色的树,每个节点恰好可以染一种颜色.初始时,所有节点的颜色都为0.现在需要实现两种操作: 1. 改变节点x的颜色为y: 2. 询问整棵树被划分成了多少棵颜色相同的子树.即每棵子树内的节点颜色都相同,而相邻子树的颜色不同. 输入 第一行一个整数T,表示数据组数,以下是T组数据. 每组