hdu 1394 Minimum Inversion Number(成段更新)
来源:互联网 发布:机械甩棍淘宝 编辑:程序博客网 时间:2024/06/07 02:57
题意:给你区间在[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),在用一重循环,求出最小的值。
两种方法:
第一种:暴力,两重循环,求出每一组逆序数之和
<span style="font-size:18px;">#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#define maxn 5555using 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|1using 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;}
0 0
- hdu 1394 Minimum Inversion Number(成段更新)
- HDU 1394 Minimum Inversion Number(单点更新)
- hdu 1394 Minimum Inversion Number(单点更新)
- hdu 1394 Minimum Inversion Number(单点更新)
- hdu 1394 Minimum Inversion Number(单点更新)
- hdu 1394 Minimum Inversion Number(单点更新,区间求和)
- hdu 1394 Minimum Inversion Number(线段树单点更新)
- hdu(1394)Minimum Inversion Number
- Minimum Inversion Number(HDU 1394)
- HDOJ 1394 Minimum Inversion Number 线段树 : 单点更新 成段求和
- hdu 1394 Minimum Inversion Number
- hdu 1394 Minimum Inversion Number
- hdu 1394 Minimum Inversion Number
- HDU 1394 Minimum Inversion Number
- HDU-1394-Minimum Inversion Number
- HDU 1394 - Minimum Inversion Number
- HDU 1394 Minimum Inversion Number
- Hdu 1394 Minimum Inversion Number
- 按指定编码读写文件
- c#中Invoke方法
- caffe 练习1:training LeNet on MNIST with Caffe/ 用LeNet识别手写字符集 Mnist------by 香蕉麦乐迪
- makefile相关总结
- HTTP POST GET 本质区别详解
- hdu 1394 Minimum Inversion Number(成段更新)
- OC 日志打印
- Android数据存储五种方式总结
- How-to: parsing XML with Qt
- vnd.ms-excel.numberformat:@"
- Xcode设置项之Architectures和Valid Architectures
- MFC对话框以ADO的方式连接(ACESS)数据库
- 何谓线程?何谓进程?何谓多线程?
- 代理模式