洛谷P5929 [POI1999]地图

洛谷题目链接

零、题目原文

一个人口统计办公室要绘制一张地图。

由于技术的原因只能使用少量的颜色。两个有相同或相近人口的区域在地图应用相同的颜色。例如一种颜色 \(k\),则 \(A(k)\) 是相应的数,则有:

在用颜色\(k\) 的区域中至少有一半的区域的人口不大于 \(A(k)\)

在用颜色\(k\) 的区域中至少有一半的区域的人口不小于 \(A(k)\)

区域颜色误差是该区域的人口与 \(A(k)\) 差的绝对值。累计误差是所有区域颜色误差的总和。我们要求出一种最佳的染色方案,使得累计误差最小。

\(10 < n < 3000\)\(2 ≤ m ≤ 10\)
人口数 \(< 2^{30}\)

一、简明题意

\(n\) 个区域,每个区域人口是\(a[i]\)\(m\) 种颜色,现在要对所有区域着色。

\(A(k)\) 即是中位数。(因为有一半区域不大于或不小于,所以肯定不大于与不小于的个数相等,即为中位数)

区间误差即为连续同样颜色区间的每个数与中位数的绝对值之和,累计误差即为所有区间误差之和。

二、思路

1、朴素转移

这题题目都讲了区间,明显的区间DP......

\(f[i][j]\) 表示考虑了前\(i\) 个区域,已经用了\(j\) 种颜色的最小误差

则枚举 \(k\), \(f[i][j]\) 一定是从前k个区间的最小值再加上从\(k+1\)\(i\) 的区间误差,

状态转移方程式:\(f[i][j] = min(f[i][j], f[k][j - 1] + color(k + 1, i))\) ,其中\(color\) 即为区间误差之和。

时间复杂度:\(O(N ^ 3M)\)

很明显,这个巨大的复杂度会T飞,所以需要一些优化

2、求值优化

再思考优化\(color\) ,可以先前缀和\(sum\) 求出每一段的人口数,而中位数即为排好序后中间两个数的平均数。

所以区间误差之和不难得出,可以得到以下\(O(1)\) 的柿子:

\(a[mid] * (mid - l) - sum[mid - 1] + sum[l - 1] - a[mid] * (r - mid) + sum[r] - sum[mid]\) ,(其中mid即为中间那个位置,不是中位数!)

所以可以把这一个柿子单独写个函数拉出来,可以将时间复杂度优化到\(O(N ^ 2M)\)

三、code

注意开始需要先排序,这样求中位数可以优化。

#include <bits/stdc++.h>
using namespace std;
const int N = 3005;
const int M = 15;
int n, m;
int f[N][M], sum[N], a[N];
int color(int l, int r)
{
	int mid = (l + r) / 2;//求中位数的位置
	return a[mid] * (mid - l) - sum[mid - 1] + sum[l - 1] - a[mid] * (r - mid) + sum[r] - sum[mid]; //O(1)优化
}              
int main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}
	sort(a + 1, a + n + 1);
	for(int i = 1; i <= n; i++)
	{
		sum[i] = sum[i - 1] + a[i];//前缀和预处理
	}
	memset(f, 0x3f, sizeof f);//开始全赋最大值
	f[0][0] = 0;
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++)
		{
			for(int k = 0; k < i; k++)
			{
				f[i][j] = min(f[i][j], f[k][j - 1] + color(k + 1, i));//转移方程
			}
		}		
	}
	cout << f[n][m];
	return 0;
}
posted @ 2020-10-22 21:55  panjx  阅读(123)  评论(0编辑  收藏  举报