zoukankan      html  css  js  c++  java
  • 算法练习

    1、输入一串数 等于某值的数的个数——hashmap的使用

    我的做法,先读入这一串数,遍历一遍找出等于某个值的数的个数,超时(空间复杂度低但时间复杂度高)

    正确做法,在读入这一串数的同时创建一个hanshmap key为值、value为个数,直接找出等于某个值的数的个数

    import java.util.*;
    public class Main{
        public static void main(String[]args){
            Scanner in=new Scanner(System.in);
            while(in.hasNext()) {
                LinkedHashMap<Integer,ArrayList<Integer>>map=new LinkedHashMap<>();
                int n=in.nextInt();
                int[]a=new int[n];
                for(int i=0;i<n;i++) {
                    a[i]=in.nextInt();
                    if(map.containsKey(a[i])) {
                        map.get(a[i]).add(i+1);
                    }else {
                        ArrayList<Integer>list=new ArrayList<Integer>();
                        list.add(i+1);
                        map.put(a[i], list);
                    }
                }
                int q=in.nextInt();
                for(int i=0;i<q;i++) {
                    int l=in.nextInt();
                    int r=in.nextInt();
                    int k=in.nextInt();
                    int count=0;
                    ArrayList<Integer>list=map.get(k);
                    if(list!=null&&list.size()!=0) {
                        for(Integer num:list) {
                            if(num>=l&&num<=r)
                                count++;
                        }
                    }
                    System.out.println(count);
                }
            }
        }
    }

    2、字符串S由小写字母构成,长度为n。定义一种操作,每次都可以挑选字符串中任意的两个相邻字母进行交换。询问在至多交换m次之后,字符串中最多有多少个连续的位置上的字母相同?——区间动态规划

    要求移动后形成的最长连续子串,这个最长连续子串可能全是ab……c。因此,这里需要枚举移动后形成的最长连续子串里面所包含的字母;

    确定了里面包含的字母,就可以专注于这个字母了,也就是说其余的字母都是没有用的,把它们从序列中挖掉;然后就值剩下目标字母了,目标字母离散地分布在序列中,因此,再离散化一下,搞完之后会生成一个行的序列,之后的动态规划就在新的序列上进行。

    下面的图片表达了上述过程:

    这里写图片描述

    现在新的序列pos看起来是合在了一起,形成了最长连续子序列,但是,形成这些连续序列所需要的操作次数是多少呢?如果操作次数大于m,那么该序列就是不满足要求的;

    因此,这里面就可以得出区间动态规划了,先从小到大枚举段长,依次求得该段长的所有子序列的操作次数,并判断是否小于等于m,如果满足要求,就更新答案。

    从小到大枚举段长是为了利用子问题的结果;dp[i][j]表示把pos[i]pos[j]之间的目标字母移动到一起,形成j - i + 1长度的连续子序列所需要的操作次数;

    状态转移方程:dp[i][i + len - 1] = dp[i + 1][i + len - 2] + pos[i + len - 1] - pos[i] - len + 1;,依据是|xa|+|xb||x−a|+|x−b|在什么时候取得最小值。用最小的移动次数把两个目标字母移动到一起的方法就是把两个目标字母都往中间靠,状态转移方程就是根据这个来的,先把pos[i + 1] ~ pos[i + len - 2]之间的目标字母移动到一起,这个移动次数就是dp[i + 1][i + len - 2],然后把两个端点pos[i]pos[i + len -1]处的目标字母往中间靠,所需要的移动次数是pos[i + len - 1] - pos[i] - len + 1

    3、

    /**
    * 有n个房间,现在i号房间里的人需要被重新分配,分配的规则是这样的:先让i号房间里的人全都出来,接下来按照 i+1, i+2, i+3, ...
    * 的顺序依此往这些房间里放一个人,n号房间的的下一个房间是1号房间,直到所有的人都被重新分配。
    *
    * 现在告诉你分配完后每个房间的人数以及最后一个人被分配的房间号x,你需要求出分配前每个房间的人数。数据保证一定有解,若有多解输出任意一个解。
    *
    *
    * 输入描述: 第一行两个整数n, x (2<=n<=10^5, 1<=x<=n),代表房间房间数量以及最后一个人被分配的房间号; 第二行n个整数
    * a_i(0<=a_i<=10^9) ,代表每个房间分配后的人数。
    *
    *
    * 输出描述: 输出n个整数,代表每个房间分配前的人数。
    *
    * 输入例子1: 3 1 6 5 1
    *
    * 输出例子1: 4 4 4
    *
    * @param args
    */

    首先明确一点,最初被重新分配的房间一定是人数最少的房间(或之一)。因为里面的人都出来后人数为0,而下一次加1要等到所有其他房间都加1之后。

    读入的同时找出人数最少的房间的人数(因为人数最少的房间有多个,还不能通过这一步就确定最初被分配的房间是哪个)。

    每个房间同时减去该人数。

    然后设置一个游标,从最后一个人被分配的房间开始,每次给当前房间-1,然后给一个临时变量temp+1,然后游标向左移动,直到找到第一个人数为0的房间,最初被分配的房间就是它了!

    然后将临时变量temp加到最初房间的人数中,再将刚才减去的最少的房间的人数加回来。

    import java.util.*;
     
    public class Main{
         
         
        public static void main(String[]args){
            Scanner in=new Scanner(System.in);
            while(in.hasNext()) {
                int n=in.nextInt();
                int x=in.nextInt();
                long[]a=new long[n];
                long sum=0;
                long minnum=Integer.MAX_VALUE;
                for(int i=0;i<n;i++) {
                    a[i]=in.nextInt();
                    if(minnum>a[i]) {
                        minnum=a[i];
                    }
                }
                for(int i=0;i<n;i++) {
                    a[i]-=minnum;
                    sum+=minnum;
                }
                int pointer=x-1;
                while(true) {
                    if(a[pointer]==0)break;
                    a[pointer]--;
                    sum++;
                    if(pointer==0)
                        pointer=n-1;
                    else
                        pointer--;
                }
                a[pointer]=sum;
                for(int i=0;i<n;i++) {
                    if(i==n-1)
                        System.out.println(a[i]);
                    else
                        System.out.print(a[i]+" ");
                }
            }
        }
     
         
    }
  • 相关阅读:
    POJ 1775 (ZOJ 2358) Sum of Factorials
    POJ 1844 Sum
    HDOJ 1081(ZOJ 1074) To The Max(动态规划)
    HDOJ 2012 素数判定
    HDOJ 2011 多项式求和
    HDOJ 2010 水仙花数
    马云最新发言:让员工、客户、合作伙伴比自己更强
    乐视手机1S正式发售,乐视商城官网抽风遭网友吐槽
    C++引用(Reference)
    实验三 二叉树的基本操作(建立)及遍历
  • 原文地址:https://www.cnblogs.com/zealousness/p/8831159.html
Copyright © 2011-2022 走看看