分级

给定序列A 构造序列非严格单调的B 使\(\sum|a_i-b_i|\)最小

搜索->dp 关键在引理:
b中的数一定在a中都出现过
所以可以离散化考虑 只考虑出现过的数

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=2010;
const int INF=0x3f3f3f3f;
int read()
{
	int x=0,f=0,c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return f?-x:x;
}
int n,tot;
int a[N],b[N],f[N][N];
//f(i,j)=min{f(i-1,k)+|ai-j|} 0<=k<=j
int main()
{
	n=read();
	for(int i=1;i<=n;i++) b[i]=a[i]=read();
	sort(b+1,b+n+1);
	tot=unique(b+1,b+n+1)-(b+1);
	for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
	for(int i=1;i<=n;i++) f[1][i]=abs(b[a[1]]-b[i]);
	for(int i=2;i<=n;i++)
	{
		int minn=INF;
		for(int j=1;j<=tot;j++) 
		{
			minn=min(minn,f[i-1][j]);
			f[i][j]=minn+abs(b[a[i]]-b[j]);
		}
	}
	int ret=INF;
	for(int i=1;i<=tot;i++) ret=min(ret,f[n][i]);
	for(int i=2;i<=n;i++)
	{
		int minn=INF;
		for(int j=tot;j>=1;j--)
		{
			minn=min(minn,f[i-1][j]);
			f[i][j]=minn+abs(b[a[i]]-b[j]);
		}
	}
	for(int i=1;i<=tot;i++) ret=min(ret,f[n][i]);	
	printf("%d\n",ret);
	 
	return 0;
}

思考题:
把A改成单调不下降的,最少需要修改:
A的总长度-A的最长 不下降子序列
把A改成单调上升的:
\(B_i-A_i-i\) 用吧的长度减去最长不下降子序列长度
原因: A中的位置不一定足够满足严格上升这一个条件 由于两个数之间最少相差i 因此把i减去 就可以让条件相互转化

已经出现过的 可以考虑用离散化来优化这个过程

posted @ 2022-02-08 14:01  __iostream  阅读(36)  评论(0)    收藏  举报