蓝桥杯2016年压缩变换
小明最近在研究压缩算法。
他知道,压缩的时候如果能够使得数值很小,就能通过熵编码得到较高的压缩比。
然而,要使数值很小是一个挑战。
最近,小明需要压缩一些正整数的序列,这些序列的特点是,后面出现的数字很大可能是刚出现过不久的数字。对于这种特殊的序列,小明准备对序列做一个变换来减小数字的值。
变换的过程如下:
从左到右枚举序列,每枚举到一个数字,如果这个数字没有出现过,刚将数字变换成它的相反数,如果数字出现过,则看它在原序列中最后的一次出现后面(且在当前数前面)出现了几种数字,用这个种类数替换原来的数字。
比如,序列(a1,a2,a3,a4,a5)=(1,2,2,1,2)在变换过程为:
a1:1未出现过,所以a1变为-1;
a2:2未出现过,所以a2变为-2;
a3:2出现过,最后一次为原序列的a2,在a2后,a3前有0种数字,所以a3变为0;
a4:1出现过,最后一次为原序列的a1,在a1后,a4前有1种数字,所以a4变为1;
a5:2出现过,最后一次为原序列的a3,在a3后,a5前有1种数字,所以a5变为1.
现在,给出原序列,请问,按这种变换规则变换后的序列是什么。
输入格式:
。输入第一行包含一个整数N,序列表示的长度
第二行所有游戏Ñ个正整数,表示输入序列。
输出格式:
输出一行,包含ñ个数,表示变换后的序列。
例如,输入:
5
1 2 2 1 2
程序应该输出:
-1 -2 0 1 1
再例如,输入:
12
1 1 2 3 2 3 1 2 2 2 3 1
程序应该输出:
-1 0 -2 -3 1 1 2 2 0 0 2 2
数据规模与约定
对于30%的数据,n <= 1000;
对于50%的数据,n <= 30000;
对于100%的数据,1 <= n <= 100000,1 <= ai <= 10 ^ 9
资源约定:
峰值内存消耗(含虚拟机)<256M
CPU消耗<3000ms
import java.util.HashMap; import java.util.Map; import java.util.Scanner; public class 压缩变换 { static Map <Integer,Integer> lastIndex =new HashMap<Integer,Integer>(); static int []b; static SegTree root; static int n; static int[] data; static int[] ans; public static void main(String arg[]) { Scanner sc=new Scanner(System.in); n=sc.nextInt(); data=new int[n]; ans=new int[n];//存放答案 b= new int [n];//存放0 1 root=buildSegTree(0,n-1);//初始化树 for(int i=0;i<n;i++) { int num=sc.nextInt(); data[i]=num; //如果没出现过,则变为-1; if(lastIndex.get(num)==null) { ans[i]=-num; b[i]=1; update(root,i,1); } else { int index=lastIndex.get(num); ans[i]=query(root,index+1,i-1); b[index]=0; b[i]=1; update(root,index,-1); //原来的出现的区间和减1 update(root,i,1); //现在的下标的区间和加1 } lastIndex.put(num,i); } for(int j=0;j<n;j++) { System.out.print(ans[j]+" "); } } //查询区间和是多少 private static int query(SegTree tree,int l,int r) { if(l<=tree.l&&r>=tree.r) { return tree.sum; } int mid=(tree.l+tree.r)/2; int ans=0; if(l<=mid) { ans+=query(tree.lson,l,r); } if(r>mid) { ans+=query(tree.rson,l,r); } return ans; } //初始化树状数组 private static SegTree buildSegTree(int l,int r) { SegTree tree=new SegTree(l,r); if(l==r) { tree.sum=b[l]; return tree; } int mid=(l+r)/2; tree.lson=buildSegTree(l,mid); tree.rson=buildSegTree(mid+1,r); tree.sum=tree.lson.sum+tree.rson.sum; return tree; } //每次更新b之后,相应的更新tree private static void update(SegTree tree,int p,int i) { if(tree==null) return; tree.sum+=i; int l=tree.l; int r=tree.r; int mid=(l+r)/2; if(p<=mid) { update(tree.lson,p,i); } else if(p>mid) { update(tree.rson,p,i); } } static class SegTree { int l,r;//左右区间 int sum;//和 SegTree lson;//左子树 SegTree rson;//右子树 public SegTree(int l,int r) { this.l=l; this.r=r; } } }
思路
这里的优化性能的方法是树状数组
并且将检查种类的问题转换为求两个索引之间区间和的问题。
首先第一次出现一个数字之后,数组b会将这个数字对应的下标里面的值设为1,当再次出现的时候,将再次出现的的设为1,之前的改为0,这样如果求两个索引之间的数字的种类数时,只需对两个索引之间的区间进行求和。
树状数组最初初始化时与b中的元素保持一致,它保存的是在不同区间(0,n-1)(二分区间将树伸展向下)的和,当b改变时,树状数组也进行update函数进行更新数值。
lastIndex是一个map,它的key是数字,value是这个数字上一次出现的下标。每次要及时更新数字的最近的上一次出现的下标位置。
注意在查询区间和的时候,在对左右子树进行递归查询的时候,参数左右区间的下标可以一直是最初的l和r,虽然这样看起来区间超过,但是并不影响出口的判断,大于当前树的区间时会返回当前树的sum和。
- 蓝桥杯 第七届 java B组压缩变换
- 第七届蓝桥杯省赛JAVA B组-压缩变换
- 备考蓝桥杯(21)压缩变换
- 第七届蓝桥杯java 压缩变换
- 蓝桥杯 第七届省赛试题 压缩变换
- 2016年蓝桥杯省赛A组C/C++ 第二题 跳蚱蜢(dfs搜索+状态压缩)
- 第七届蓝桥杯javaB组第十题:压缩变换(链表实现)
- 第七届蓝桥杯JavaB组-压缩变换
- 交换瓶子 - 2016年第七届蓝桥杯
- 蓝桥杯 表格计算 2016年第六届蓝桥杯javaB组决赛第五题
- 2016年第七届蓝桥杯c/c++省赛B组
- 2016年蓝桥杯
- 蓝桥杯-【煤球数目】【2016年省赛B组题解】【C++】
- 2016年蓝桥杯解析--平方怪圈(java)
- 2016年蓝桥杯比赛心得
- 2016年蓝桥杯决赛 奇怪的数列
- 2016年蓝桥杯C++ A组初赛试题
- 2016年蓝桥杯A组java第六题
- 2016年蓝桥杯java——凑算式
- 基于DCT变换的JPEG图像压缩原理