ACM中的几个小技巧(离散化,尺取法,数据预处理)

7 篇文章 0 订阅

离散化
使用STL算法离散化:
思路:先排序,再删除重复元素,然后就是索引元素离散化后对应的值。
假定待离散化的序列为a[n],b[n]是序列a[n]的一个副本,则对应以上三步为:

sort(sub_a,sub_a+n);
int size=unique(sub_a,sub_a+n)-sub_a;//size为离散化后元素个数
for(i=0;i<n;i++)
a[i]=lower_bound(sub_a,sub_a+size,a[i])-sub_a + 1;
//k为b[i];

    ```


经离散化后对应的值
对于第3步,若离散化后序列为0, 1, 2, ..., size - 1则用lower_bound,从1, 2, 3, ..., size则用upper_bound,其中lower_bound返回第1个不小于b[i]的值的指针,而upper_bound返回第1个大于b[i]的值的指针,当然在这个题中也可以用lower_bound然后再加1得到与upper_bound相同结果,两者都是针对以排好序列。使用STL离散化大大减少了代码量且结构相当清晰。

**尺取法**
给定长度为n的数列整数a0,a1,a2,a3 ..... an-1以及整数S。求出综合不小于S的连续子序列的长度的最小值。如果解不存在,则输出0。
  10 < n< 10 ^ 50 < a i < 10^4 S<10^8
这里我们拿第一组测试数据举例子,即 n=10, S = 15, a = {5,1,3,5,10,7,4,9,2,8}
![](http://images.cnitblog.com/blog/597004/201408/291224259702079.jpg)
1.初始化左右端点
2.不断扩大右端点,直到满足条件
3.如果第二步中无法满足条件,则终止,否则更新结果
4.将左端点扩大1,然后回到第二步

例题:poj 3320 尺取法+Map

``
#include <map>
#include <queue>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define  maxn 100005
#define  MOD 1000000007
#define  inf  0x3f3f3f3f
typedef long long ll;
using namespace std;
int n,m,k;
int a[maxn];

int main(){

    while(~scanf("%d",&n)){
        map<int,int>Hash,cnt;
        for(int i = 0;i<n;i++){
            scanf("%d",a+i);
            Hash[a[i]]++;
        }
        int tot = Hash.size();
        int tag = 1;
        int s = 0,e = 0;
        int ans = inf;
        while(tag){
            while(cnt.size()<tot && e<n) cnt[a[e++]]++;
            if(cnt.size()<tot) {tag=0;break;}
            ans = min(ans,e-s);
            cnt[a[s]]--;
            if(cnt[a[s]]==0) cnt.erase(a[s]);
            s++;
        }
        printf("%d\n",ans);

    }
    return 0;
}

数据预处理
以HDU 5073 鞍山D为例。
主要是针对区间的求和,平方这类的预处理
ans=min(ans,xix0)2+x202x0xi)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 51000
using namespace std;
double sum1,sum2;
double pos[maxn];
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i = 1;i<=n;i++)
            scanf("%lf",pos+i);
        if(n==k){puts("0");continue;}
        sort(pos+1,pos+1+n);
        sum1 = sum2 = 0;
        int m = n-k;
        for(int i = 1;i<m;i++){
            sum1 += pos[i];
            sum2 += pos[i] * pos[i];
        }
        double ans = 1e100;

        for(int i = m;i<=n;i++){
            sum1 += pos[i];
            sum2 += pos[i]*pos[i];
            double mid = sum1 / m;
            double tmp = sum2 + m*mid*mid - 2*mid*sum1;
            ans = min(ans,tmp);
            sum1 -= pos[i-m+1];
            sum2 -= pos[i-m+1] * pos[i-m+1];
        }
        printf("%.12f\n",ans);
    }
    return 0;
}
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值