洛谷P1966火柴排队

这是一道逆序对变形题

可以这样理解题意:给你一个数组a一个数组b  然后b是一个偏序集合来定义这题的大小比较

比如现在给你a数组为 1 3 2 4  b数组为3 1 2 4

那如果以b为偏序集合的话这题里就有3<1<2<4  然后由1<2<3<4可知

其实3的真实值应该是1才对 1的真实值应该是2才对 2的真实值应该是3才对 4的真实值应该是4才对

那我们就可以知道a数组的真实值应该是 2 1 3 4  然后在真实值的情况下只要a按1<2<3<4排好就等同于a的原值 1 3 2 4 按3<1<2<4排好了

然后问题就转化成了如何将2 1 3 4排成1 2 3 4然后花费最少步数 由于题目是问要最少多少步可以达到目的  那就说明肯定是有答案的 也就是可排的 那我们每次就只需要找不符合排序方式的相邻的逆序对就行调转就好 这样就能将逆序对减一   直到没有逆序对了(也就是按要求排好了)

所以最终问题就转化成了将a的值改成真实值 最终求它的逆序对总数就是最少的移动步数了

关于逆序对的求法如果有不会的建议先做洛谷p1908再来做这题

实现于下:

#include <iostream>
#include <algorithm>
using namespace std;
const int mod=99999997;
struct node
{
    int value, num;//value是题目给的值  num是它的编号
    bool friend operator<(node a, node b)
    {
        return a.value < b.value;
    }
};
node a[100005];
node b[100005];
int f[100005];
int t[100005];
int fun(int f[], int l, int r)//此函数为纯纯求f数组[l,r]的逆序对模板
{
    if (l >= r)
        return 0;
    int mid = (l + r) >> 1;
    int x = fun(f, l, mid)%mod;
    int y = fun(f, mid + 1, r)%mod;//记得取模
    int tmp = 0;
    for (int i = l, j = l, k = mid + 1; i <= r; i++)
    {
        if (j == mid + 1)
            t[i] = f[k++];
        else if (k == r + 1)
        {
            t[i] = f[j++];
            tmp += i - j + 1;
            tmp%=mod;
        }
        else
        {
            if(f[j]<f[k])
            {
                t[i]=f[j++];
                tmp+=i-j+1;
                tmp%=mod;
            }
            else
            {
                t[i]=f[k++];
            }
        }
    }
    for(int i=l;i<=r;i++)
    f[i]=t[i];
    return (x+y+tmp)%mod;
}
int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i].value);
        a[i].num = i;
    }
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &b[i].value);
        b[i].num = i;
    }
    sort(a + 1, a + 1 + n);
    sort(b + 1, b + 1 + n);
    //a[i].num是找出它原本在第几个 然后改成按b的偏序集的偏好(即真实值)即可
    for (int i = 1; i <= n; i++)
        f[a[i].num] = b[i].num;
    //得到a的真实值数组丢去求逆序对数量
    cout<<fun(f, 1, n)<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值