hdu 1394 Minimum Inversion Number(成段更新)
2015-10-15 10:25
274 查看
题意:给你区间在[0,n-1]的整数,也就是n个数排成一行,下标从0开始到n-1,让你求它的逆序数之和,然后每次将最左边的的数移到末尾,重新求逆序数,这样有n组,让你求最小的逆序数之和,所谓逆序数,就是下标i<j,但数值确是x[i]>x[j],让你求这样的数有几个。
比如 : 3 1 2 4 0
下标比三小,数值比三大的数没有,则对三来说满足条件的是零个
然后考虑1,下标比一小,数值比一大,対一来说满足的为1个
同理 对2来说有一个,对四来说没有,对零来说有4个,所以逆序数个数为6个
这样一组逆序数就求出来了。
题目要求的是n组中逆序数的最小和,下面有一个规律:
每一次把第一个数移到最右边,原来比他小的数都构不成逆序数,比它大的数重新成为逆序数,所以要加上比它大的数,减去比他小的数,还有一个技巧,就是给定的是连续的数,假设第一个数值为m,要把它移到末尾,因为总共有n个数,下标从0 开始,所以,比m大的数就是(n-m-1)个,比m小的数就是m个,所以得到递推公式 sum =sum +(n-1+m)-(m),在用一重循环,求出最小的值。
两种方法:
第一种:暴力,两重循环,求出每一组逆序数之和
第二种:线段树
输入一个点,就把他与之对应的叶子节点标记为1,说明这个点已经插入,这样插入可以表明插入的先后,所以每次查询的只要查询当前点到n-1这段区间,若有标记为一的,就说明这是满足条件的数,逆序数和加一。
比如 : 3 1 2 4 0
下标比三小,数值比三大的数没有,则对三来说满足条件的是零个
然后考虑1,下标比一小,数值比一大,対一来说满足的为1个
同理 对2来说有一个,对四来说没有,对零来说有4个,所以逆序数个数为6个
这样一组逆序数就求出来了。
题目要求的是n组中逆序数的最小和,下面有一个规律:
每一次把第一个数移到最右边,原来比他小的数都构不成逆序数,比它大的数重新成为逆序数,所以要加上比它大的数,减去比他小的数,还有一个技巧,就是给定的是连续的数,假设第一个数值为m,要把它移到末尾,因为总共有n个数,下标从0 开始,所以,比m大的数就是(n-m-1)个,比m小的数就是m个,所以得到递推公式 sum =sum +(n-1+m)-(m),在用一重循环,求出最小的值。
两种方法:
第一种:暴力,两重循环,求出每一组逆序数之和
<span style="font-size:18px;">#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #define maxn 5555 using namespace std; int x[maxn]; int main() { int n,sum,a; while(~scanf("%d",&n)) { sum=0; for(int i=0;i<n;i++) scanf("%d",&x[i]); for(int i=n-1;i>0;i--) { for(int j=0;j<i;j++) if(x[i]<x[j]) sum++; //每次循环找出比当前数下标比它小,数值比它大的数,次数加一 } // printf("%d\n",sum); int ans=sum; for(int i=0;i<n;i++) sum+=(-x[i])+(n-x[i]-1),ans=min(ans,sum); printf("%d\n",ans); } }</span><span style="font-size:24px;"> </span>
第二种:线段树
输入一个点,就把他与之对应的叶子节点标记为1,说明这个点已经插入,这样插入可以表明插入的先后,所以每次查询的只要查询当前点到n-1这段区间,若有标记为一的,就说明这是满足条件的数,逆序数和加一。
#include<iostream> #include<cstdio> #define maxn 5555 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; int sum[maxn<<2]; int x[maxn]; void pushup(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; //向上更新逆序数的和 } void build(int l,int r,int rt) { sum[rt]=0; //初始化,建立一个空的线段树 if(l==r) return; int m=(l+r)>>1; build(lson); build(rson); } int query(int L,int R,int l,int r,int rt) { if(L<=l&&r<=R) return sum[rt]; //每次询问,插入的点作为区间左端点,n-1作为区间右端点 int m=(l+r)>>1; int ret=0; if(L<=m) ret+=query(L,R,lson); if(R>m) ret+=query(L,R,rson); return ret; } void update(int p,int c,int l,int r,int rt) { if(l==r) { sum[rt]=c; //每次插入一个数,就标记为c return ; } int m=(l+r)>>1; if(p<=m) update(p,c,lson); else update(p,c,rson); pushup(rt); } int main() { int n; while(~scanf("%d",&n)) { int sum=0; build(0,n-1,1); //题意区间为[0,n-1] for(int i=0;i<n;i++) //每次输入一个点,先询问序号比他小,数值比他大的数的数数值和,就把他插入到线段树内 { scanf("%d",&x[i]); sum+=query(x[i],n-1,0,n-1,1); //询问 update(x[i],1,0,n-1,1);//更新 } int ans = sum; //printf("%d",ans); for(int i=0;i<n;i++) //递推 { sum+=(-x[i])+(n-x[i]-1); ans=min(ans,sum); //不断求最小的值 } printf("%d\n",ans); } return 0; }
相关文章推荐
- iOS之通知传值的使用
- OpenWRT GPIO人口控制 WLED
- 使用T4模板生成代码的学习
- 技术文摘13 郑钧 泰岳 技术 资料 源码 UI tinypng 保质压缩 github 大神 高仿 VVDocumenter-Xcode 工具 唐巧
- .公司域名总量TOP15 :新网、万网、中资源稳居前三
- C++读写文件
- Android笔记8 SharedPreference
- CloudStack 4.4+KVM之虚拟机模板创建
- 在VS中使用TinyFox调试OWIN应用(转)
- 【正则表达式】js删除末尾的0
- 解决eclipse创建Maven项目后无法生成src/main/java资源文件夹的方法
- c#相关连接
- 正则表达式
- jquery查看当前平台
- Microsoft Office 2016 官方简体中文正式版 ISO镜像(VOL)下载
- Microsoft Office 2016 官方简体中文正式版 ISO镜像(VOL)下载
- Microsoft Office 2016 官方简体中文正式版 ISO镜像(VOL)下载
- 那几个月在找工作(百度、网易游戏等)
- String,StringBuffer,StringBuilder的区别
- 常用正则表达式大全 (转)