hash一下 + 归并排序计算逆序对数

OJ题目:click here~~

题目分析:给一个序列,选择两个数,交换这两个数的位置,使得交换后,序列的逆序数最少。给出交换后的逆序数。

先将序列中的数hash一下,是为了下面方便计算数的次数。

归并排序,求逆序数。

枚举所有可选的两个数a , b,计算这两个数之间,比a大的数,比a小的数,比b大的数,比b小的数。交换a , b 之后 ,逆序数 = 原来的逆序数 + 比a大的数 + 比b小的数 - 比a小的数 - 比b大的数 + a,b逆序数的变化。

AC_CODE

const int Max_N = 1002;

int x[Max_N] , h[Max_N] , sum ;
int dpless[Max_N][Max_N] ;//dp[i][j];到i处为止,比j小的数的个数
int dpbig[Max_N][Max_N] ;//dpbig[i][j]:到i处为止,比j大的数的个数

void Merg(int L , int R){
    if(L == R) return ;
    int M = (L + R)>>1;
    Merg(L , M);
    Merg(M + 1 , R);
    for(int i = M + 1;i <= R;i++){
        sum += (x + 1 + M) - upper_bound(x + L , x + 1 + M , x[i]);
    }
    sort(x + L , x + R + 1);
}

int main(){
    int i , j , k , n;
    while(scanf("%d",&n) != EOF){
        for(i = 1;i <= n;i++){
            scanf("%d",&x[i]);
            h[i] = x[i];
        }
        //先hash一下
        sort(x + 1 , x + 1 + n);//需要先排序
        int len = unique(x + 1, x + 1 + n) - (x + 1);//不同的元素的个数
        for(i = 1;i <= n;i++)
            h[i] = lower_bound(x + 1 , x + 1 + len , h[i]) - x;
        for(i = 1;i <= n;i++) x[i] = h[i];
        sum = 0 ;
        Merg(1 , n) ;//求原来序列的逆序数
        if(!sum){
            puts("0") ;
            continue;
        }
        memset(dpless , 0 , sizeof(dpless));
        memset(dpbig , 0 , sizeof(dpbig));
        for(i = 1;i <= n;i++) x[i] = h[i];
        for(i = 1;i <= n;i++){
            for(j = 1;j <= len;j++)
                dpless[i][j] = dpless[i - 1][j];
            for(j = x[i] + 1;j <= len;j++)
                dpless[i][j]++;
            for(j = 1;j <= len;j++)
                dpbig[i][j] = dpbig[i - 1][j];
            for(j = 1;j < x[i];j++)
                dpbig[i][j]++;
        }

        int ans = sum;
        int a , b , aless , bless , abig , bbig ;
        for(i = 1;i <= n;i++){
            for(j = i + 1;j <= n;j++){
                a = x[i];
                b = x[j];
                bless = dpless[j - 1][b] - dpless[i][b];
                bbig = dpbig[j - 1][b] - dpbig[i][b];
                abig = dpbig[j - 1][a] - dpbig[i][a];
                aless = dpless[j - 1][a] - dpless[i][a];
                int k = 0 ;
                if(a < b) k = 1;
                if(b < a) k = -1;
                ans = min(ans , sum + bless + abig - aless - bbig + k);
            }
        }
        cout << ans << endl;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值