数据--dp,线段树优化
题目描述
Mr_H 出了一道信息学竞赛题,就是给 n 个数排序。输入格式是这样的:试题有若干组数据。每组数据的第一个是一个整数 n,表示总共有 n 个数待排序;接下来 n 个整数,分别表示这 n 个待排序的数。
例如:3 4 2 –1 4 1 2 3 4,就表示有两组数据。第一组有 3 个数(4,2,-1),第二组有 4个数(1,2,3,4)。可是现在 Mr_H 做的输入数据出了一些问题。例如:2 1 9 3 2 按理说第一组数据有 2 个数(1,9),第二组数据有 3 个数,可是“3”后面并没有出现三个数,只出现了一个数“2”而已!
现在 Mr_H 需要对数据进行修改,改动中“一步”的含义是对文件中的某一个数+1 或-1,写个程序,计算最少需要多少步才能将数据改得合法。
输入
第一行一个整数 m,表示 Mr_H 做的输入数据包含的整数个数。第二行包含 m 个整数 a[i],每个整数的绝对值不超过 10000。
输出
一个整数,表示把数据修改为合法的情况下,最少需要多少步。
样例输入
4
1 9 3 2
样例输出
2
提示
对于 20%的数据,m<=10, |a[i]|<=5;
对于 60%的数据,m<=5000, |a[i]|<=10000
对于 100%的数据,m<=100000, |a[i]|<=10000
容易想到f[i]:a[1]-a[i]合法最小的步数,f[i]=min{f[k]+|i-(k+1)-a[k]|}。时间复杂度为O(n^2)
如果从后往前考虑,f[j]:a[j]-a[n]合法的最小步数,f[j]=min{f[k]+| (a[j]+j+1) -k|}.
分类讨论:
(1).(a[j]+j+1)>=j ,f[j]=min{f[k]-k} + (a[j]+j+1));
(2).(a[j]+j+1)
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 100000
#define INF 2000000000
struct node{
int l,r,minf[3];
}tree[MAXN*4+100];
int n,a[MAXN+10],f[MAXN+10];
//f[i]:a[i]~a[n]改合法要用的最小步数
void read()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
}
void build(int i,int l,int r)
{
tree[i].l=l,tree[i].r=r;
if(l==r) return ;
int mid=(l+r)/2;
build(i*2,l,mid);
build(i*2+1,mid+1,r);
}
void insert(int i,int pos,int d,int flag)
{
if(tree[i].l==pos && tree[i].r==pos){
tree[i].minf[flag]=d;
return ;
}
int mid=(tree[i].l+tree[i].r)/2;
if(pos<=mid) insert(i*2,pos,d,flag);
else insert(i*2+1,pos,d,flag);
tree[i].minf[flag]=min(tree[i*2].minf[flag],tree[i*2+1].minf[flag]);
}
int Quary(int i,int l,int r,int flag)
{
if(r<tree[i].l || tree[i].r<l) return INF;
if(l<=tree[i].l && tree[i].r<=r){
return tree[i].minf[flag];
}
int x=Quary(i*2,l,r,flag);
int y=Quary(i*2+1,l,r,flag);
return min(x,y);
}
void dp()
{
insert(1,n+1,-(n+1),1);
insert(1,n+1,n+1,2);
for(int i=n;i>=1;i--){
int tmp=a[i]+i+1,x=-1,y=-1;
if(tmp<i+1)
y=Quary(1,i+1,n+1,2),f[i]=y-tmp;
else if(tmp+1<=n+1){
x=Quary(1,i+1,tmp,1);
y=Quary(1,tmp+1,n+1,2);
f[i]=min(x+tmp,y-tmp);
}
else
x=Quary(1,i+1,n+1,1),f[i]=x+tmp;
//特别注意这里的范围
insert(1,i,f[i]-i,1);
insert(1,i,f[i]+i,2);
}
}
int main()
{
read();
build(1,1,n+1);
dp();
printf("%d\n",f[1]);
return 0;
}