HDU 1394 Minimum Inversion Number

本文探讨了如何计算一个序列在进行特定操作后逆序数最小的序列,并输出其最小逆序数的个数。利用线段树和树状数组进行高效查找和更新操作,适用于序列长度不超过5000的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

        给定一个int型序列,长度为n(n ≤ 5,000),序列就是数字0 ~ n - 1的一个排列,将序列首元素放到末尾就可以得到一个新的序列,重复这样操作n此就又回到了原序列,求这n个序列中逆序数最小的序列,并输出其最小逆序数的个数,测例数为t。

题目链接

线段树:

注释代码:

/*                                             
 * Problem ID : HDU 1394 Minimum Inversion Number
 * Author     : Lirx.t.Una                                             
 * Language   : C                            
 * Run Time   : 31 ms                                             
 * Run Memory : 228 KB                                             
*/ 

#include <memory.h>
#include <stdio.h>

//序列最大长度
#define    MAXN        5000

#define    LFT(T)        ( (T) << 1 )
#define    RHT(T)        ( LFT(T) | 1 )

short    a[MAXN];//存放
//根结点区间为0 ~ n
//按顺序查询一个数的逆序数时,如果一个数为a
//则查询区间a + 1 ~ n之间数的个数就行了
//注意末尾要多留一个n,因为查n - 1时,查询区间的左端点为n
//左端点不能大于右端点,所以右端点是n而不是n - 1
short    seg[MAXN << 1];//线段树

int
query( int tree, int ql, int qr, int lft, int rht ) {
	
    int        mid;
	
    if ( lft == ql && qr == rht ) return seg[tree];
	
    mid = ( lft + rht ) >> 1;
    
    if ( qr <= mid ) return query( LFT(tree), ql, qr, lft, mid );
    else if ( ql > mid ) return query( RHT(tree), ql, qr, mid + 1, rht );
    else return query( LFT(tree), ql, mid, lft, mid ) +
		query( RHT(tree), mid + 1, qr, mid + 1, rht );
}

void
insert( int tree, int v, int lft, int rht ) {
	
    int        mid;
	
    seg[tree]++;
	
    if ( lft != rht ) {
		
        mid = ( lft + rht ) >> 1;
        if ( v <= mid ) insert( LFT(tree), v, lft, mid );
        else insert( RHT(tree), v, mid + 1, rht );
    }
}

int
min( int a, int b ) {
	
    return a < b ? a : b;
}

int
main() {
	
    int        n;//序列长度
	
    int        i;
	
    int        inv;//逆序数
    int        ans;//最后的答案
	
    while ( ~scanf("%d", &n) ) {
		
        inv = 0;
        memset(seg, 0, sizeof(seg));
		
        for ( i = 0; i < n; i++ ) {
			
            scanf("%d", a + i);
            inv += query( 1, a[i] + 1, n, 0, n );
            insert( 1, a[i], 0, n );
        }
		
        ans = inv;
        for ( i = 0; i < n - 1; i++ )
			//如果首元素为a[i],则放到末尾后逆序数的变化量为
			//n - 1 - 2a[i]
            ans = min( ans, inv += n - 1 - ( a[i] << 1 ) );
		
        printf("%d\n", ans);
    }
	
    return 0;
}
无注释代码:

#include <memory.h>
#include <stdio.h>

#define    MAXN        5000

#define    LFT(T)        ( (T) << 1 )
#define    RHT(T)        ( LFT(T) | 1 )

short    a[MAXN];
short    seg[MAXN << 1];

int
query( int tree, int ql, int qr, int lft, int rht ) {

    int        mid;

    if ( lft == ql && qr == rht ) return seg[tree];

    mid = ( lft + rht ) >> 1;
    
    if ( qr <= mid ) return query( LFT(tree), ql, qr, lft, mid );
    else if ( ql > mid ) return query( RHT(tree), ql, qr, mid + 1, rht );
    else return query( LFT(tree), ql, mid, lft, mid ) +
                query( RHT(tree), mid + 1, qr, mid + 1, rht );
}

void
insert( int tree, int v, int lft, int rht ) {

    int        mid;

    seg[tree]++;

    if ( lft != rht ) {
    
        mid = ( lft + rht ) >> 1;
        if ( v <= mid ) insert( LFT(tree), v, lft, mid );
        else insert( RHT(tree), v, mid + 1, rht );
    }
}

int
min( int a, int b ) {

    return a < b ? a : b;
}

int
main() {

    int        n;

    int        i;

    int        inv;
    int        ans;

    while ( ~scanf("%d", &n) ) {
    
        inv = 0;
        memset(seg, 0, sizeof(seg));

        for ( i = 0; i < n; i++ ) {
        
            scanf("%d", a + i);
            inv += query( 1, a[i] + 1, n, 0, n );
            insert( 1, a[i], 0, n );
        }

        ans = inv;
        for ( i = 0; i < n - 1; i++ )
            ans = min( ans, inv += n - 1 - ( a[i] << 1 ) );

        printf("%d\n", ans);
    }

    return 0;
}

树状数组:

注释代码:

/*                                             
 * Problem ID : HDU 1394 Minimum Inversion Number
 * Author     : Lirx.t.Una                                             
 * Language   : C                            
 * Run Time   : 31 ms                                             
 * Run Memory : 228 KB                                             
*/ 

#include <memory.h>
#include <stdio.h>

#define    MAXN        5001

short    a[MAXN];
//树状数组
//c[i]表示数字1 ~ i区间中数字的个数
short    c[MAXN];

short    lowbit[MAXN];

int        n;//序列长度

void
insert(int x) {
	
    while ( x <= n ) {
		
        c[x]++;
        x += lowbit[x];
    }
}

int
query(int x) {
	
    int        sum;
	
    sum = 0;
    while ( x > 0 ) {
		
        sum += c[x];
        x   -= lowbit[x];
    }
	
    return sum;
}

int
min( int a, int b ) {
	
    return a < b ? a : b;
}

int
main() {
	
    int        inv;
    int        ans;
	
    int        i;
	
    for ( i = 1; i < MAXN; i++ ) lowbit[i] = i & -i;//打表
	
    while ( ~scanf("%d", &n) ) {
		
        inv = 0;
        memset(c, 0, sizeof(c));
		
        for ( i = 1; i <= n; i++ ) {
			
            scanf("%d", a + i);
            a[i]++;//树状数组下标从1开始
			
            insert(a[i]);
			//由于是最后插入a[i]的,插入a[i]后总共有i个数
			//而1 ~ a[i]的数有query(a[i])个,因此在a[i]之前大于a[i]的数就有
			//i - query(a[i])个,a[i]现在在序列的末尾处!!!
            inv += i - query(a[i]);
        }
		
        ans = inv;
        for ( i = 1; i < n; i++ )//由于下标从1开始算,因此表达式有一点变化
			//a[i]变成了a[i] - 1
            ans = min( ans, inv += n - 1 - ( ( a[i] - 1 ) << 1 ) );
		
        printf("%d\n", ans);
    }
	
    return 0;
}
无注释代码:

#include <memory.h>
#include <stdio.h>

#define    MAXN        5001

short    a[MAXN];
short    c[MAXN];

short    lowbit[MAXN];


int        n;

void
insert(int x) {

    while ( x <= n ) {
    
        c[x]++;
        x += lowbit[x];
    }
}

int
query(int x) {

    int        sum;

    sum = 0;
    while ( x > 0 ) {
    
        sum += c[x];
        x   -= lowbit[x];
    }

    return sum;
}

int
min( int a, int b ) {

    return a < b ? a : b;
}

int
main() {

    int        inv;
    int        ans;

    int        i;

    for ( i = 1; i < MAXN; i++ ) lowbit[i] = i & -i;

    while ( ~scanf("%d", &n) ) {
    
        inv = 0;
        memset(c, 0, sizeof(c));

        for ( i = 1; i <= n; i++ ) {
        
            scanf("%d", a + i);
            a[i]++;

            insert(a[i]);
            inv += i - query(a[i]);
        }

        ans = inv;
        for ( i = 1; i < n; i++ )
            ans = min( ans, inv += n - 1 - ( ( a[i] - 1 ) << 1 ) );

        printf("%d\n", ans);
    }

    return 0;
}
单词解释:

inversion:n, 倒置,反转

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值