• 火柴排队


     

    题目描述

     

    涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为:∑(ai-bi)2

    其中ai 表示第一列火柴中第i个火柴的高度,bi表示第二列火柴中第 i 个火柴的高度。

    每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对99,999,997取模的结果。

     

    输入格式

     

    共三行,第一行包含一个整数n,表示每盒中火柴的数目。

    第二行有n个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。

    第三行有n个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。

     

    输出格式

     

    一个整数,表示最少交换次数对 99,999,997 取模的结果。

     

    输入输出样例

     

    输入 #1
    4
    2 3 1 4
    3 2 1 4
    输出 #1
      1

     

    输入 #2
    4
    1 3 4 2
    1 7 2 4
    输出 #2
      2

     

    说明/提示

     

    【输入输出样例说明1】

    最小距离是0,最少需要交换 次,比如:交换第 1列的前2 根火柴或者交换第 2 列的前 2根火柴。

    【输入输出样例说明2】

    最小距离是 10,最少需要交换2次,比如:交换第1 列的中间22根火柴的位置,再交换第2 列中后 2 根火柴的位置。

    【数据范围】

    对于 10\%的数据, 1 ≤ n ≤ 10

    对于 30\%的数据,1 ≤ n ≤ 100

    对于 60\%的数据,1 ≤ n ≤ 1,000

    对于 100\%的数据,1 ≤ n ≤ 100,000,0 ≤火柴高度≤ maxlongint

     

    引导:

    首先看到这道题我们,我们先好好动下脑经想:如何排序火柴才能将∑(a_i−b_i)^2 最大值求出来。


    思路 :

    所以我们应该容易想到将两个队列先进行排序,比如说将两个队列的最大的元素对齐,次大的元素对齐,以此类推,就可以发现 | a - b | 的值就会是最小的,那么这样 ∑(ai-bi)2的值是最小的。

    这里我们提供两种代码:线段树 和 树状数组 (其实是同一种思想

     

    如何实现代码:

    1. 使用是树状数组时前面的模板是通用的,然后主函数理只需要将两个数组排序,再进行前面说到的匹配,就好了。

    2. 使用线段树时,也是一样先模板敲好,接下来最最关键的就是离散化, 观察题目,我们可以发现,所谓的求最少次数交换位置,因为离散化好后,不就是求这个数组中的逆序对?于是,代码就明确了!

    何谓离散化?离散化就是不在乎这个数组内具体某一个值的大小(在这里我们称之为绝对数值),而是在乎这个数组内的相对大小(相对数值,也可以理解为排序后的排名)。比如下面有两个数组

    A :100 200 399 488

    B :938 132 144 77

    在这里我们可以看到A【1】 = 488,A【2】 = 399,以此类推,说明A数组的离散化后 488** 所对应的值就是 1。 B 数组同理。

    那么我们就可以很轻松的敲出代码即可。

    若还是有不懂离散化的,下面代码里有详解。

    (个人意见:跟着代码走更好理解。)


    二维偏序做法: 

    我们都知道将两个序列按增序排列时可得到最小距离,但这样可能有些多余的交换(比如两个序列同时将两个相同位置的数作了相同的交换,交换到相同位置)

    做法:先将一个序列变成增序,另一个序列跟着做同样的交换(可用结构体),得到一个序列,则将该序列变为增序的操作次数(也就是求该序列的逆序对数)就是将两个序列变为增序两两对应的操作次数


    树状数组:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #define mod 99999997
    using namespace std;
    typedef long long LL;
    int n, ans;
    int c[1000009], d[1000009];
    int a[1000009], b[1000009];
    int q[1000009];
    bool cmp1(int i, int j) {return a[i] < a[j];}
    bool cmp2(int i, int j) {return b[i] < b[j];}
    
    namespace Tree{
        int low[1000009];
    
        LL lowbit(LL x) {return -x&x;}
    
        LL sum(LL x){
            LL ret = 0;
            while (x > 0){
                ret += low[x];
                x -= lowbit(x);
            }
            return ret;
        }
    
        void add(LL x, LL d){
            while (x <= n){
                low[x] += d;
                x += lowbit(x);
            }   
        }
    }
    using namespace Tree;
    
    void solve()
    {
        ans = 0;
        for (int i = n; i >= 1; i--){
            ans += sum(q[i] - 1);
            add(q[i], 1);
            if (ans >= mod) ans -= mod;
        }
        printf("%d", (ans % mod + mod) % mod);
    }
    
    int main()
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)    scanf("%d", &a[i]), c[i] = i;
        for (int i = 1; i <= n; i++)    scanf("%d", &b[i]), d[i] = i;
        sort(c + 1, c + n + 1, cmp1);
        sort(d + 1, d + n + 1, cmp2);
        memset(q, 0, sizeof(q));
        for (int i = 1; i <= n; i++){
            q[c[i]] = d[i];
        }
        solve();
        return 0;
     } 


     

    线段树:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cmath>
    #include<set>
    #include<cstring>
    using namespace std;
    //head
    //时间复杂度: O(nlogn)
    //这个题目又是求逆序对的。。。
    #define lsn l,mid,rt<<1
    #define rsn mid+1,r,rt<<1|1
    const int N = 1000005;
    const int mod = 99999997;
    int n;
    int v[N], a[N], b[N], c[N], pos[N], tree[N<<2];
    void add(int l, int r, int rt, int p, int v){
        if(l == r){
            tree[rt] += v;
            return;
        }
        int mid = (l + r) >> 1;
        if(p <= mid) add(lsn, p, v);
        else add(rsn, p, v);
        tree[rt] = tree[rt<<1|1] + tree[rt<<1];
    }
    int query(int l, int r, int rt, int p){
        if(r <= p) return tree[rt];
        if(l > p) return 0;
        int ans = 0;
        int mid = (l + r) >> 1;
        ans += query(lsn, p);
        ans += query(rsn, p);
        return ans;
    }
    int main(){
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i){
            scanf("%d", a+i);
            v[i] = a[i];
        }
        sort(v+1, v+n+1);
        for(int i = 1; i <= n; ++i){
             a[i] = lower_bound(v+1, v+n+1, a[i]) - v;  //离散化,就是我们只需要a[i]中每个元素的相对排名,并不关心它们的具体的数值大小
             pos[a[i]] = i;                         //记录排名第a[i]的位置
        }
        for(int i = 1; i <= n; ++i){
            scanf("%d", b+i);
            v[i] = b[i];
        }
        sort(v+1, v+n+1);
        for(int i = 1; i <= n; ++i) b[i] = lower_bound(v+1, v+n+1, b[i]) - v;   //同样对b数组进行离散化
        for(int i = 1; i <= n; ++i){
            c[i] = pos[b[i]];               //用c数组来表示b数组中的第i个元素 应该放置的位置
        }
        //求逆序对
        long long ans = 0;
        for(int i = 1; i <= n; ++i){
            int num = query(1, n, 1, c[i]);
            ans = (ans + i - 1 - num) % mod;
            add(1, n, 1, c[i], 1);
        }
        printf("%lld
    ", ans);
        return 0;
    }
  • 相关阅读:
    Vagrant安装virtualbox
    SQLSERVER排查CPU占用高的情况
    删除重复记录,只留一条
    ASCII码对应表chr(9)、chr(10)、chr(13)、chr(32)、chr(34)、chr(39)、……
    手机和PC端的录屏软件
    2017年初面试总结
    Python面向对象
    Python字体颜色
    Python第二模块总结
    Fiddler使用教程(转)
  • 原文地址:https://www.cnblogs.com/aprincess/p/11625527.html
Copyright © 2020-2023  润新知