bzoj 1049: [HAOI2006]数字序列(DP+DP)

1049: [HAOI2006]数字序列

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 1848   Solved: 801
[ Submit][ Status][ Discuss]

Description

  现在我们有一个长度为n的整数序列A。但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列。
但是不希望改变过多的数,也不希望改变的幅度太大。

Input

  第一行包含一个数n,接下来n个整数按顺序描述每一项的键值。n<=35000,保证所有数列是随机的

Output

  第一行一个整数表示最少需要改变多少个数。 第二行一个整数,表示在改变的数最少的情况下,每个数改变
的绝对值之和的最小值。

Sample Input

4
5 2 3 5

Sample Output

1
4


第一问很简单,对于下标x<y,当且仅当a[y]-a[x]>=y-x时,下标y和x的两个数就可以都不动,否则一定有一个要修改

设b[i] = a[i]-i,F[i]表示以b[i]结尾的LIS长度,答案就是n-F[n]


第二问可以证出对于下标x<y,且F[x]+1=F[y],那么一定存在一个k∈(x, y)满足

(x, k]里的数全部修改成a[x]+1, a[x]+2,a[x]+3,…,a[x]+k-x;

(k, y)里的数全部修改成a[y]-(y-k+1),…,a[y]-2,a[y]-1

即[x, k]内所有的a[]连续,(k, y]内所有的a[]连续时幅度最优

设dp[i]表示前i个数修改成严格递增序列的最小幅度,其中第i个数a[i]不变

那么有dp[i] = min(dp[j]+Mincost(k),  j<i && F[j]+1=F[i] && b[j]<=b[i])

其中Mincost也可以直接暴力k∈(x, y),这样复杂度为O(n^3),但是由于数据完全随机,所以能进行转移的(i, j)非常少!

不过为了保证正确,要在数组最前面加一个非常小的数,在数组最后面加一个非常大的数


O(可以过),程序还可以再优化

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define LL long long
LL b[35005], a[35005], bet[35005], F[35005], dp[35005];
int main(void)
{
	LL n, i, len, l, j, k, r, m, now, Min;
	scanf("%lld", &n);
	for(i=1;i<=n;i++)
	{
		scanf("%lld", &a[i]);
		b[i] = a[i]-i;
	}
	len = 0;
	a[0] = b[0] = -1e10;
	a[n+1] = b[n+1] = 1e10;
	for(i=1;i<=n+1;i++)
	{
		if(len==0 || bet[len]<=b[i])
			bet[++len] = b[i], F[i] = len;
		else
		{
			l = 1, r = len;
			while(l<r)
			{
				m = (l+r)/2;
				if(bet[m]<=b[i])
					l = m+1;
				else
					r = m;
			}
			bet[r] = b[i];
			F[i] = r;
		}
	}
	memset(dp, 62, sizeof(dp));
	printf("%lld\n", n-(len-1));
	dp[0] = 0;
	for(i=1;i<=n+1;i++)
	{
		for(j=0;j<=i-1;j++)
		{
			if(F[j]+1!=F[i] || b[j]>b[i])
				continue;
			now = 0;
			for(k=j+1;k<=i-1;k++)
				now += abs(a[k]-(a[i]-(i-k)));
			Min = now;
			for(k=j+1;k<=i-1;k++)
			{
				now = now-abs(a[k]-(a[i]-(i-k)))+abs(a[k]-(a[j]+k-j));
				Min = min(Min, now);
			}
			dp[i] = min(dp[i], dp[j]+Min);
		}
	}
	printf("%lld\n", dp[n+1]);
	return 0;
}
/*
5
1 1 1 1 1
*/


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值