您的位置:首页 > 其它

[BZOJ2933]数据

2017-07-24 21:13 99 查看
题目描述

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

题解:

dp[i]: 使num[1]~num[i]的数串合法, 最少需要多少步

dp[i]=min{ dp[j]+abs( num[j+1]-(i-j-1) ) } ( j<i )

时间为O( n^2 ),不加优化绝对T;

将abs分开算:

① dp[i]=min{ dp[j]+num[j+1]-i+j+1 } ( j<i≤num[j+1]+j+1 )

② dp[i]=min{ dp[j]+i-j-1-num[j+1] } ( j<i && num[j+1]+j+1<i )

[法一]用单调队列优化:

① dp[i]=min{ dp[j]+( num[j+1]+j+1 )-i } ( j<i≤num[j+1]+j+1 )

② dp[i]=min{ dp[j]-( num[j+1]+j+1 )+i } ( j<i && num[j+1]+j+1<i )

一开始先将num[j+1]+j+1处理出来。

先算①: 用优先队列维护, 开两个变量, 一个记录dp[j]+num[j+1], 一个记录num[j+1]

当算②时, 可直接由①变过来: ②=①-num[j+1]*2=q.top().a-q.top().b*2

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=1e5+10;
struct node {
int a, b; node(){} node( int x, int y ) { a=x; b=y; }
bool operator < ( const node &x ) const { return a>x.a; }
};
priority_queue< node >q;
int n, dp
, num
, minz;

int main() {
scanf( "%d", &n );
for( int i=1; i<=n; i++ ) scanf( "%d", &num[i] ), num[i]+=i;

q.push( node( num[1], num[1] ) );
memset( dp, INF, sizeof dp );
minz=INF;

for( int i=1; i<=n; i++ ) {
while( !q.empty() && q.top().b<i )
minz=min( minz, q.top().a-q.top().b*2 ), q.pop();
if( !q.empty() ) dp[i]=min( dp[i], q.top().a-i );
dp[i]=min( dp[i], minz+i );
q.push( node( dp[i]+num[i+1], num[i+1] ) );
}

printf( "%d\n", dp
);
return 0;
}


[法二]用线段树优化:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=1e5+10;
int n, num
, ans;
struct node{ int l, r, min1, min2; }tree[N<<2];

void Build( int l, int r, int pos ) {
tree[pos].l=l; tree[pos].r=r;
tree[pos].min1=tree[pos].min2=INF;
if( l==r ) return;
int mid=( l+r )>>1;
Build( l, mid, pos<<1 );
Build( mid+1, r, pos<<1|1 );
}

void Insert( int x, int pos, int d ) {
if( tree[pos].r<x || x<tree[pos].l ) return;
tree[pos].min1=min( tree[pos].min1, d-x );
tree[pos].min2=min( tree[pos].min2, d+x );
if( tree[pos].l==tree[pos].r ) return;
Insert( x, pos<<1, d );
Insert( x, pos<<1|1, d );
}

int Find( int l, int r, int pos, bool flg ) {
if( tree[pos].r<l || r<tree[pos].l ) return INF;
if( l<=tree[pos].l && tree[pos].r<=r ) {
if( flg ) return tree[pos].min1;
return tree[pos].min2;
}
return min( Find( l, r, pos<<1, flg ), Find( l, r, pos<<1|1, flg ) );
}

int main() {
scanf( "%d", &n );
for( int i=1; i<=n; i++ ) scanf( "%d", &num[i] ), num[i]+=i;
Build( 1, n+1, 1 );
Insert( n+1, 1, 0 );
for( int i=n; i; i-- ) {
ans=min( Find( i+1, min( num[i], n+1 ), 1, 1 )+num[i]+1, Find( num[i]+1, n+1, 1, 0 )-num[i]-1 );
Insert( i, 1, ans );
}
printf( "%d\n", ans );
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息