AcWing273 分级

先上题目:

给定长度为 N 的序列 A,构造一个长度为 N 的序列 B,满足:

1. B 非严格单调,即 B1≤B2≤…≤BN或 B1≥B2≥…≥BN。

2. 最小化 S=∑Ni=1|Ai−Bi|

只需要求出这个最小值 S。

输入格式

第一行包含一个整数 N。

接下来 N 行,每行包含一个整数 Ai。

输出格式

输出一个整数,表示最小 S 值。

数据范围

1≤N≤2000  ;  0≤Ai≤10^6

分析:

首先,要先获得一条性质:所有的B[i]都能在A[i]中找到。(反正我并没有觉得很显然,我是想不到这东西的,刚开始往平均数那里去想了)

证明这个性质吧:

先画出这个图,然后想啊,现在考虑没有在线上的圈圈,怎么把他们挪动线上呢?当A[k](表示粉色框框里面的圈圈所对应的A[i])小于A`[i](在这里指A`[1])的个数大于 大于A`[i+1](在这里指A`[2])的个数时,向下移动,这样,整体来看,差距应该是变小的。反之则向上移动。这样,在有限次的移动下,所有的圆圈都能移到线上,而且差距一定逐渐变小。

然后就要用的dp了,假设dp[i][j]表示的含义是第i个元素为A`[j]时,S的最小值。所以:

#include<iostream>
#include<algorithm>
using namespace std;
const int INF = 0x3f3f3f3f, N = 2005;
int a[N], b[N], n, dp[N][N];
int work()
{
	for (int i = 1; i <= n; i++)b[i] = a[i];
	sort(b + 1, b + n + 1);
	for (int i = 1; i <= n; i++)
	{
		int vmin = INF;
		for (int j = 1; j <= n; j++)//当i一定时,循环,得出最后第i个元素取不同值时的最小值
		{
			vmin = min(vmin, dp[i - 1][j]);//这里可以将i-1时的最小情况得出来,从而运用到i时。
			dp[i][j] = vmin + abs(a[i] - b[j]);
		}
	}
	int end = INF;
	for (int i = 1; i <= n; i++)end = min(end, dp[n][i]);//遍历,寻找j为几时,s最小
	return end;
}
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)cin >> a[i];
	int summary = work();
	reverse(a + 1, a + n + 1);//翻转过来,当B[i]递减时
	summary = min(summary, work());//取递增递减两种情况的最小值。
	cout << summary << endl;
	return 0;
}

第一次真正意义上做DP,真的很难想到啊,要这样构造,主要是不知道怎样创造规则,dp一般就是运用一个二维数组,然后在里面再进行操作,主要是自己要找到规则。 其次就是这种题自己要找性质,想偏了就很难做出来

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值