zoukankan      html  css  js  c++  java
  • HDU 1394 Minimum Inversion NumberMinimum Inversion Number(线段树)

    http://acm.hdu.edu.cn/showproblem.php?pid=1394

    部分来自http://blog.csdn.net/libin56842/article/details/8531117

    写给那些 刚入门线段树,开始和我一样对解题迷茫的人.

    题意 求最小逆序数

    逆序数的概念

    在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。逆序数为偶数的排列称为偶排列;逆序数为奇数的排列称为奇排列。如2 4 3 1中,21,43,41,31是逆序,逆序数是4,为偶排列。

    题目要求序列做如下0 到 n-1变换后, 求出逆序数分别是多少, 输出 最小的逆序数

    如:0 3 4 1 2

    设逆序数初始n = 0;

    由于0后面没有比它小的,n = 0

    3后面有1,2 n = 2

    4后面有1,2,n = 2+2 = 4;

    所以该序列逆序数为 4

    其根据题意移动产生的序列有

    3 4 1 2 0   逆序数:8

    4 1 2 0 3  逆序数:6

    1 2 0 3 4  逆序数:2

    2 0 3 4 1  逆序数:4

    所以最小逆序数为2


    思路:

    首先题目规定 输入的n个数是0到n-1的,不会大于等于n。假设一个序列 a[n],,逆序总数是 m , 第一个数是x, 那么x的后面有 x 个数比 x 小,因为包含0, 则第一个数移动最后一个,则 x 的 前面 有 n - x - 1个比 x 大, 所以这时候 逆序总数就是 m - x + n - x - 1;以此枚举每一个 i 移到最后的情况。 所以,我们只用知道初始序列的逆序数是多少即可, 当然方法很多,这里讲一下线段树。如何建树,每输入一个数a[i],就查询 比这个数大的区间[a[i]+1, n]内,有多少个数 前面 已经输入过了, 就是存在,那么用线段树便可以解决了。

    代码

    #include <stdio.h>
    #include <iostream>
    #include <string.h>
    using namespace std;
    
    #define manx 5005
    struct Tree
    {
    	int left; 
    	int right;
    	int num; //该区间内,已经出现的个数
    }tree[4 * manx];
    int a[manx];
    
    void BuildTree(int i, int l, int r);
    void Update(int i, int id);
    int Query(int i, int l, int r);
    
    int main()
    {
    	int n;
    	while(~scanf("%d", &n))
    	{
    		int ans = 0;
    		BuildTree(1, 1, n);
    		for(int i = 0; i < n; i++)
    		{
    			scanf("%d", &a[i]);
    			Update(1, a[i] + 1);
    			ans += Query(1, a[i] + 2, n);//询问比他大区间已经出现的次数
    			//printf("ans = %d
    ", ans);
    		}
    		int m = ans;
    		for(int i = 0; i < n; i++)
    		{
    			m = m + n - 2 * a[i] - 1;
    			if(m < ans)
    				ans = m;
    		}
    		printf("%d
    ", ans);
    	}
    	return 0;
    }
    
    void BuildTree(int i, int l, int r)//建树
    {
    	tree[i].left = l;
    	tree[i].right = r;
    	tree[i].num = 0;//初始化全部为0,因为还没有输入
    	if(tree[i].left == tree[i].right) return;
    	int mid = (l + r) >> 1;
    	BuildTree(i << 1, l, mid);
    	BuildTree(i << 1 | 1, mid + 1, r);
    }
    
    void Update(int i, int id)//更新
    {
    	if(tree[i].left == id && tree[i].right == id)
    	{
    		tree[i].num = 1;//输入过了,标记为1
    		return;
    	}
    	int mid = (tree[i].left + tree[i].right) >> 1;
    	if(id > mid)  Update(i << 1 | 1, id);
    	else   Update(i << 1, id);
    	tree[i].num = tree[i << 1].num + tree[i << 1 | 1].num;//更新此区间内,已经出现过的总数
    }
    
    int Query(int i, int l, int r)//询问该区间已经出现的次数
    {
    	//printf("%d %d %d
    ", i, l, r);
    	if(l > r) return 0;
    	if(tree[i].left == l && tree[i].right == r) return tree[i].num;
    	int mid = (tree[i].left + tree[i].right) >> 1;
    
    	if(r <= mid) return Query(i << 1, l, r);
    	else if(l > mid) return Query(i << 1 | 1, l, r);
    	else return Query(i << 1, l, mid) + Query(i << 1 | 1, mid + 1, r);
    }                                                        //这里是mid+1
    

      

     

  • 相关阅读:
    源码分析:①ReentrantLock之公平锁和非公平锁
    源码分析:同步基础框架——AbstractQueuedSynchronizer(AQS)
    java 同步锁:synchronized 关键字
    Java 中的锁优化
    Java 魔法类:Unsafe 解析
    实战:SpringBoot 2.3.0新特性之优雅停机
    实战:windows下使用docker部署springboot web demo项目
    版本控制SVN的使用笔记
    Maven项目管理:SpringMVC+Mybatis+Velocity整合笔记
    java常用集合总结
  • 原文地址:https://www.cnblogs.com/tenlee/p/4420107.html
Copyright © 2011-2022 走看看