• 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
    

      

     

  • 相关阅读:
    c# 如何制作RealPlayer 视频播放器
    【.Net】在C#中判断某个类是否实现了某个接口
    【EF】Entity Framework 6新特性:全局性地自定义Code First约定
    【EF】EF实现大批量数据库插入操作
    【数据库】各种主流 SQLServer 迁移到 MySQL 工具对比
    【Python】python学习之总结
    【Asp.Net Core】ASP.NET Core 2.0 + EF6 + Linux +MySql混搭
    【.Net】Visual Studio的调试技巧
    【转载】用C#编写一个简单的记事本
    【ADO.NET】ADO.NET知识点
  • 原文地址:https://www.cnblogs.com/tenlee/p/4420107.html
Copyright © 2020-2023  润新知