#bzoj2933#【重庆市NOIP模拟赛】数据(DP线段树优化 or DP堆优化 + 证明)

31 篇文章 0 订阅
13 篇文章 0 订阅

2933: 【重庆市NOIP模拟赛】数据

时间限制:1 Sec  内存限制:128 MB

题目描述

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]表示以i为最后一组的结尾,合法的最小更改次数

Dp[ i ] = Dp[ j ] + abs( A[j + 1] - (i - j - 1) );

Dp[ i ] = (Dp[ j ] + A[j + 1] + j) - (i - 1);(A[j + 1] + j >= i - 1)

Dp[ i ] = (Dp[ j ] - A[j + 1] - j) + (i  - 1);(A[j + 1] + j <= i - 1)

考虑用线段树优化或者堆优化。


先说线段树吧,好理解。

build两棵线段树。

就是在[1, A[j + 1] + j]中找一个最小的Dp[j] + A[j + 1] + j

在[A[j + 1] + j, N]中找一个最小的Dp[j] - A[j + 1] - j

然后通过上面的公式算出来之后取min

下标可以离散化也可以不离散化,都可以A。


再说堆优化,有两种堆优化,一种是两个堆的,一种是一个堆的。

首先是一个堆的,将DP变化为:

Dp[i] = Dp[j] + (A[j + 1] + j + 1) - i;(A[j + 1] + j + 1 >= i)

Dp[i] = Dp[j] - (A[j + 1] + j + 1) + i;(A[j + 1] + j + 1 <= i)

用一个堆,维护Dp[j] + A[j + 1] + j + 1最小, 

将堆根里A[j + 1] + j + 1 < i的弹出,并记录下最小的

minnum = Q.top().val - Q.top().pos * 2 = Dp[j] + (A[j + 1] + j + 1) - 2 * (A[j + 1] + j + 1) = Dp[j] - (A[j + 1] + j + 1);


看起来似乎没有将所有的满足A[j + 1] + j + 1 < i的Dp[j] + .....的值都拿出来比较,会不会得出的值并不一定是当前的最小值呢?

(感觉好像被压在下面没有弹出来,感觉可能由这个k转移出来的Dp[i]的值更优,因为堆里是Dp[k] + A[k + 1] + k + 1最小,而这时的k的转移是Dp[i] = Dp[k] - (A[k + 1] + k + 1) + i,似乎会漏情况)

这个并不影响最后的结果,因为会有更优的代替,证明如下:


假设现在j为堆根,k在堆下面,而k是满足A[k + 1] + k + 1 < i的

已知:Dp[j] + A[j + 1] + j + 1 <= Dp[k] + A[k + 1] + k + 1

且 A[j + 1] + j + 1 >= i(令其为a),A[k + 1] + k + 1(令其为b) < i

移项得 : Dp[j] + a <= Dp[k] + b

Dp[j] - Dp[k] + a <= b 

证明转移后的Dp[i],由j转会比k转更优。

由j转Dp[i]:Dp[j] + A[j + 1] + j + 1 - i  = Dp[j] + a - i 

由k转Dp[i]:Dp[k] - A[k + 1] - k - 1 + i= Dp[k] - b + i 

① - ②:

Dp[j] - Dp[k] + a + b - 2 * i

由③:

Dp[j] - Dp[k] + a + b <= 2 * b

因为 b < i

所以b * 2 - 2 * i < 0

即由j转移得到的Dp[i] < 由k转移得到的Dp[i]

证毕,所以k即使被压在下面,也不影响最后的最优结果。


处理第二种情况Dp[i] = minnum + i;

注意,之前的都是Dp[j] - (A[j + 1] + j + 1)的情况,所以实际上是最小的,而因为i在增大,所以只可能向堆中放入更多的j,而之前的j将一直满足条件,所以放在minnum中。

弹出后仍留在堆根的Dp[j] + A[j + 1] + j + 1是当前满足第一式的最小值,所以处理第一式。

然后更新堆,完毕。


两个堆的优化等我搞清楚了再来写。

Code:

一个堆版本:

#include<iostream> 
#include<cstdio> 
#include<cstdlib> 
#include<cstring> 
#include<algorithm> 
#include<queue> 
using namespace std; 
  
const int Max = 100000; 
const int INF = 0x3f3f3f3f; 
  
struct node{ 
    int val, pos; 
    node(){} 
    node(int a, int b){ val = a, pos = b;} 
    bool operator < (const node & X) const{ 
        return val > X.val; 
    } 
}; 
  
int N; 
int A[Max + 5], Dp[Max + 5]; 
priority_queue<node>Q; 
  
bool getint(int & num){ 
    char c; int flg = 1;    num = 0; 
    while((c = getchar()) < '0' || c > '9'){ 
        if(c == '-')    flg = -1; 
        if(c == -1) return 0; 
    } 
    while(c >= '0' && c <= '9' ){ 
        num = num * 10 + c - 48; 
        if((c = getchar()) == -1)   return 0; 
    } 
    num *= flg; 
    return 1; 
} 
  
int main(){ 
    //freopen("data.in", "r", stdin); 
    //freopen("data.out", "w", stdout); 
    getint(N); 
    for(int i = 1; i <= N; ++ i)    getint(A[i - 1]), A[i - 1] += i; 
    Q.push(node(A[0], A[0])); 
    memset(Dp, 0x3f, sizeof Dp ); 
    int minnum = INF; 
    for(int i = 1; i <= N; ++ i){ 
        while(! Q.empty() && Q.top().pos < i){ 
            minnum = min(minnum, Q.top().val - 2 * Q.top().pos); 
            Q.pop(); 
        } 
        Dp[i] = min(Dp[i], minnum + i); 
        if(! Q.empty()) Dp[i] = min(Dp[i], Q.top().val - i); 
        Q.push(node(Dp[i] + A[i], A[i])); 
    } 
    printf("%d\n", Dp[N]); 
    return 0; 
} 


线段树版本:

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

const int Max = 100000;
const int INF = 0x3f3f3f3f;

struct node{
    int l, r;
    int val;
}Tr[2][(Max << 4) + 5];

int N;
int A[Max + 5], B[Max + 5], Dp[Max + 5];

bool getint(int & num){
    char c; int flg = 1;    num = 0;
    while((c = getchar()) < '0' || c > '9'){
        if(c == '-')    flg = -1;
    }
    while(c >= '0' && c <= '9' ){
        num = num * 10 + c - 48;
        c = getchar();
    }
    num *= flg;
    return 1;
}

void build(int i, int l, int r, bool flg){
    Tr[flg][i].l = l, Tr[flg][i].r = r;
    Tr[flg][i].val = INF;
    if(l == r)  return ;
    int mid = (l + r) >> 1;
    build(i << 1, l, mid, flg);
    build(i << 1 | 1, mid + 1, r, flg);
}

void Insert(int i, int pos, bool flg, int val){
    if(pos > Tr[flg][i].r || pos < Tr[flg][i].l)  return ;
    if(pos == Tr[flg][i].l && Tr[flg][i].r == pos){
        Tr[flg][i].val = min(val, Tr[flg][i].val);
        return ;
    }
    Insert(i << 1, pos, flg, val);
    Insert(i << 1 | 1, pos, flg, val);
    Tr[flg][i].val = min(Tr[flg][i << 1].val, Tr[flg][i << 1 | 1].val);
}

int minnum;

int Get_min(int i, int l ,int r, bool flg){
    if(r < Tr[flg][i].l || l > Tr[flg][i].r)    return minnum = INF;
    if(l <= Tr[flg][i].l && r >= Tr[flg][i].r)  return minnum = Tr[flg][i].val;
    return minnum = min(Get_min(i << 1, l, r, flg), Get_min(i << 1 | 1, l, r, flg));
}

int main(){
    getint(N);
    for(int i = 1; i <= N; ++ i)    getint(A[i]), B[i] = A[i] + i - 1;
    sort(B + 1, B + 1 + N);
    int len = unique(B + 1, B + 1 + N) - B - 1;
    int pos = lower_bound(B + 1, B + 1 + len, A[1]) - B, p;
    build(1, 1, len, 0);
    build(1, 1, len, 1);
    Insert(1, pos, 0, -A[1]);//Dp[i] = (Dp[j] - A[j + 1] - j) + i - 1
    Insert(1, pos, 1, A[1]);//Dp[i] = (Dp[j] + A[j + 1] + j) - (i - 1)
    for(int i = 1; i <= N; ++ i){
        pos = lower_bound(B + 1, B + 1 + len, i - 1) - B;
        if(B[pos] > i - 1)  p = pos - 1;
        else p = pos;
        Dp[i] = Get_min(1, 1, p, 0) + i - 1;
        Dp[i] = min(Dp[i], Get_min(1, pos, len, 1) - (i - 1));
        pos = lower_bound(B + 1, B + 1 + len, A[i + 1] + i) - B;
        Insert(1, pos, 0, Dp[i] - A[i + 1] - i);
        Insert(1, pos, 1, Dp[i] + A[i + 1] + i);
    }
    printf("%d\n", Dp[N]);
    return 0;
}






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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值