Minimum Inversion Number HDU - 1394(求一个数字环的逆序对+多种解法)

题意:

给出n个数(0~n-1,每个数仅出现一次),问它长为n的循环序列中逆序对最少的数量。

多种解法:暴力+树状数组+分治+规律推导公式

题目:

The inversion number of a given number sequence a1, a2, …, an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.

For a given sequence of numbers a1, a2, …, an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following:

a1, a2, …, an-1, an (where m = 0 - the initial seqence)
a2, a3, …, an, a1 (where m = 1)
a3, a4, …, an, a1, a2 (where m = 2)

an, a1, a2, …, an-1 (where m = n-1)

You are asked to write a program to find the minimum inversion number out of the above sequences.

Input

The input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1.

Output

For each case, output the minimum inversion number on a single line.

Sample Input

10
1 3 6 9 0 8 5 7 4 2

Sample Output

16

分析:

第一种解法:暴力

1.为了实现环,将数组扩到两倍a[i+n]=a[i];
2.求出初始串的状态,即最小逆序对;
3.由(1),直接往后遍历,每次选取连续的n个数,即效果等同于每次把最后的元素放置最前即可实现环。

AC代码

/**暴力*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int M=1e4+10;
int a[M],b[M];
int n,ans,num,cnt;
int main()
{while(~scanf("%d",&n)){memset(b,0,sizeof(b));for(int i=0;i<n;i++){scanf("%d",&a[i]);a[i+n]=a[i];}ans=0;for(int i=0;i<n;i++){for(int j=i+1;j<n;j++)if(a[i]>a[j])b[i]++;ans+=b[i];}for(int i=1;i<=n-1;i++){cnt=0;for(int j=i;j<i+n-1;j++){if(a[j]>a[i-1])b[j]++;cnt+=b[j];}if(ans>cnt)ans=cnt;}printf("%d\n",ans);}return 0;
}

第二种解法:树状数组求逆序数。

1.由题意:n个数(0~n-1,每个数仅出现一次),用树状数组来维护[0,n)的区间。
2.每次读入一个数,将该数存入树状数组b[],用树状数组来维护[0,n)的区间和。
3.每次先求出区间[ai+1,n)的和,即为ai之前比ai大的数字的个数。
4.由前面,求出初始串的状态,即最小逆序对
5.如序列a1,a2,a3,a4,a5,它的逆序对数量s=sum(num(ak>ai,k<i));序列变为a2,a3,a4,a5,a1,和上一个序列相比,变化分为两步。
(1)拿走a1,逆序对减少a1( 整个序列是0~n-1且每个数字仅出现一次)个,
(2)将a1 放入序列尾部;加入序列尾部,逆序对增加n-1-a1个,这样就可以递推求解各序列的逆序对个数。

#include<stdio.h>/**树状数组求逆序数*/
#include<string.h>
#include<algorithm>
using namespace std;
const int M=5e3+10;
int n,ans,num,mi;
int a[M],b[M];
int lowbit(int x)
{return x&(-x);
}
int query(int x)///每次先求出区间[ai+1,n)的和,即为ai之前比ai大的数字的个数
{int num=0;x+=1;while(x<=n){num+=b[x];x+=lowbit(x);}return num;
}
void update(int pos,int val)///用树状数组来维护[0,n)的区间和
{while(pos>0){b[pos]+=val;pos-=lowbit(pos);}
}
int main()
{while(~scanf("%d",&n)){ans=0;memset(b,0,sizeof(b));for(int i=0; i<n; i++){scanf("%d",&a[i]);update(a[i]+1,1);///每次读入一个数,将该数存入树状数组b[]ans+=query(a[i]+1);///查询前面是否有比该数大的数,即为逆序数}mi=ans;for(int i=0; i<n; i++){ans=ans+(n-1)-2*a[i];/**如序列a1,a2,a3,a4,a5,它的逆序对数量s=sum(num(ak>ai,k<i));
序列变为a2,a3,a4,a5,a1,和上一个序列相比,变化分为两步,拿走a1和将a1 放入序列尾部;拿走a1,逆序
对减少a1( 整个序列是0~n-1且每个数字仅出现一次)个,加入序列尾部,逆序对增加n-1-a1个,这样就可
以递推求解各序列的逆序对个数。*/if(ans<mi)mi=ans;}printf("%d\n",mi);}return 0;
}

第三种解法:分治求逆序数

1.用分治求出初始串的状态,每次把最后的元素放置最前即可实现环。
2.每次移动时:(与前面树状树状的后面解法一样)
(1)所有data[i]后面比他小的元素逆序数 -1(0~k-1,k个);
(2)所有data[i]后面比他大的元素逆序数 +1(n-1-k个);
(3)逆序数改变总数是 n - 2*k - 1(k = data[i]);
枚举不同的首元素,输出最小的逆序数即可。

#include <stdio.h>
#include <stdlib.h>
const int M=5e3+10;
int a[M];
int b[M];
int c[M];
int dfs(int aa,int bb)
{if (aa<bb){int L=dfs(aa,(aa+bb)/2);int R=dfs((aa+bb)/2+1,bb);int num=L+R,tot=aa;int x=aa,y=(aa+bb)/2;int u=(aa+bb)/2+1,v=bb;while(x<=y||u<=v)if(u<=v&&(x==y+1||a[x]>a[u])){b[tot++]=a[u++];num+=y-x+1;//计算A B 中的逆序数}elseb[tot++]=a[x++];for(int i=aa;i<=bb;++i)a[i]=b[i];return num;}elsereturn 0;
}int main()
{int n;while (~scanf("%d",&n)){for( int i = 1 ; i <= n ; ++ i )scanf("%d",&a[ i ]);for ( int i = 1 ; i <= n ; ++ i )c[ i ] = a[ i ];int ans= dfs( 1, n );int mi =ans;for ( int i = 1 ; i < n ; ++ i ){ans+= n - 2*c[ i ] - 1;if ( ans< mi )mi=ans;}printf("%d\n",mi);}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309879.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++实现拓扑排序(vector模拟邻接表存储,栈实现)

代码如下: #include<iostream> #include <vector> #include <string> #include <stack> using namespace std; const int N 10010;int in[N]; vector<int>v[N]; vector<int>printElem;int main() {int n, m;while (cin >> n >&…

如何借助Kubernetes实现持续的业务敏捷性

导语在当今以数字为动力&#xff0c;以客户为中心的世界中&#xff0c;敏捷性已成为组织提高其创建软件的速度、提高响应能力以满足不断变化的客户需求、保持敏捷性以适应变化的关键要素。敏捷不是商品&#xff0c;无法购买或轻易获得。要为组织创造可持续的敏捷性&#xff0c;…

Find them, Catch them POJ - 1703(种类并查集)

题意&#xff1a; 在这个城市里有两个黑帮团伙&#xff0c;现在给出N个人&#xff0c;问任意两个人他们是否在同一个团伙 1.输入D x y代表x于y不在一个团伙里 2.输入A x y要输出x与y是否在同一团伙或者不确定他们在同一个团伙里 题目&#xff1a; The police office in Tadu…

C++实现拓扑排序(邻接表存储,栈实现)

代码如下: #include <iostream> #include <stack> using namespace std; const int N 10010; using vnodeType int; typedef struct Node {int adj;int w;Node *next; }Node;typedef struct Vnode {int indegree;vnodeType v;Node *firstEdge; }Vnode;class Gra…

报复性降薪潮来袭

点击蓝字关注&#xff0c;回复“职场进阶”获取职场进阶精品资料一份最近不少读者问我&#xff1a;“洋哥&#xff0c;公司要全员降薪了&#xff0c;我该留下还是走呢”。今年的职场环境的确很残酷&#xff0c;不少公司直接破产&#xff0c;还有很多公司开始降薪裁员来保证现金…

linux 的网络操作与配置文件,Linux常用文件与网络操作命令速记指南

ls复制代码代码如下:$ ls #查看当前目录下文件conf lnmp_install.sh README vhost_ngx_pagespeed.shinit.sh ngx_pagespeed.sh source vhost.sh复制代码代码如下:$ ls conf #查看conf目录下文件index.html nginx.conf pureftpd-mysql.conf tz.phpinit.d.nginx pure-ftpd.conf s…

[C++11]通过using定义基础类型和函数指针别名

1.定义别名 语法: typedef 旧的类型名 新的类型名; typedef unsigned int uint_t;using 新的类型 旧的类型; using uint_t int ;通过using和typedef的语法格式可以看到二者的使用没有太大的区别&#xff0c;假如我们定义一个函数指针&#xff0c;using的优势就凸显出来了&…

基于 abp vNext 和 .NET Core 开发博客项目

介绍此个人博客项目底层基于 ABP Framework (不完全依赖)搭建项目 和免费开源跨平台的 .NET Core 3.1 开发&#xff0c;可作为 .NET Core 入门项目进行学习&#xff0c;支持各种主流数据库(SqlServer、MySQL、PostgreSql、Sqlite)接入&#xff0c;接口遵循 RESTful API 接口规范…

添加库路经 linux,linux下的静态库与动态库

文件名形如 libxxx.so&#xff0c;其中so是 Shared Object 的缩写&#xff0c;即可以共享的目标文件。生成动态库常用 gcc 命令&#xff1b;举例&#xff1a;编写头文件&#xff1a;ok.h 文件#ifndef __OK_H__#define __OK_H__voidok();#endif编写 ok.c 文件#include "ok.…

操作系统习题三

题目&#xff1a; 1.有8个程序段&#xff0c;他们之间的前驱关系如下&#xff0c;试用信号量实现这些程序段之间的同步 2.简述进程同步机制的基本原则。 答&#xff1a;在多道程序环境下&#xff0c;当程序并发执行时&#xff0c;由于资源共享和进程合作&#xff0c;使同处于…

[C++11]函数模板的默认模板参数

在C11中添加了对函数模板默认参数的支持。 代码如下: #include<iostream> using namespace std;template<typename T long ,typename U int > void myTest(T t A,U u B) {cout << "t " << t << " u " << u <…

揭秘!微软 Build 2020 开发者大会将启,邀您共赴线上新旅程

微软热爱的开发者&#xff0c;开发者热爱的新技术微软Build 2020开发者大会大幕将启行业技术大拿云集&#xff0c;全新技术重磅发布一场专属技术爱好者间的技术交流盛宴北京时间5月19日-20日&#xff0c;邀您会面&#xff01;大会年年有&#xff0c;今年何不同&#xff1f;本届…

JAVA基础知识+基础代码

Java基础知识 异常处理 try {} catch(Exception e) {} void work() throws Exception {} //抛出异常 throw new Exception("输入的字符不能为空&#xff01;"); class MyException1 extends Exception //自定义异常类 {String msg null;public MyException1(S…

linux查找应用主机,Linux 主机和服务器基本性能检查命令和工具

无论我们选择Linux 主机、服务器用来搭建网站&#xff0c;还是用来软件测试项目&#xff0c;在购买之前肯定要查看适合的性价比、配置&#xff0c;以及商家的口碑等一系列的问题。不过&#xff0c;最为重要的可能是在选择之后要进行服务器的各种性能测试&#xff0c;是否适合项…

[C++11]对模板右尖括号的优化

在泛型编程中&#xff0c;模板实例化有一个非常繁琐的地方&#xff0c;那就是连续的两个右尖括号(>>)会被编译器解析成右移操作符&#xff0c;而不是模板参数表的结束。 C11改进了编译器的解析规则&#xff0c;尽可能地将多个右尖号(>)解析成模板参数结束符&#xff…

一文带解读C# 动态拦截覆盖第三方进程中的函数(外挂必备)

一、前言由于项目需要&#xff0c;最近研究了一下跨进程通讯改写第三方程序中的方法&#xff08;运行中&#xff09;&#xff0c;把自己程序中的目标方法直接覆盖第三方程序中的方法函数&#xff1b;一直没有头绪&#xff0c;通过搜索引擎找了一大堆解决方案&#xff0c;资料甚…

JAVA JFrame编程

JFrame编程 最基础的窗口 public test1() {setTitle("test1");setSize(300,300); //设置窗口大小setLocation(300,300); //设置窗口的位置jp new JPanel();JButton jbnew JButton("button");add(jb); //整个窗口可以增加一个按钮setDefaultCloseOpera…

linux卸载docker redis,【Docker】Redis在docker上的安装、启动、停止、删除操作

查找镜像按名称搜索图像docker search redis按名称搜索并显示非截断描述(--no-trunc)docker search --stars3 --no-trunc redis按名称redis搜索出星数至少为3颗星的镜像docker search --filter stars3 redis显示名称中包含“redis”的图像&#xff0c;并且是自动构建docker sea…

[C++11]override关键字的使用

override关键字确保在派生类中声明的重写函数与基类的虚函数有相同的签名&#xff0c;同时也明确表明将会重写基类的虚函数&#xff0c;这样就可以保证重写的虚函数的正确性&#xff0c;也提高了代码的可读性&#xff0c;和final一样&#xff0c;这个关键字要写到方法的后面。 …

Sql Server之旅——第二站 理解讨厌的表扫描

很久以前我们在写sql的时候&#xff0c;最怕的一件事情就是sql莫名奇妙的超级慢&#xff0c;慢的是几根烟抽完&#xff0c;那个小球还在一直转。。。这个着急也只有当事人才明白&#xff0c;后来听说有个什么“评估执行计划“&#xff0c;后来的后来才明白应该避免表扫描。。。…