2016年赛题

2、

生日蜡烛

某君从某年开始每年都举办一次生日party,并且每次都要吹熄与年龄相同根数的蜡烛。

现在算起来,他一共吹熄了236根蜡烛。

请问,他从多少岁开始过生日party的?

请填写他开始过生日party的年龄数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

我的代码

可以直接用等差数列求和啊,首项加最后一项,除以2,成项数,但是为了防止向下取整有误差,那就先成项数,然后再除以2好一点

class test6{
    public static void main(String[] args) {
        int sum = 0;
        for(int i=1;i<100;i++){
            sum= 0;
            for(int j =i;j<100;j++){
                sum+=j;
                if(sum==236){
                    System.out.println(i);
                    break;
                }
            }
        }
    }
}

官方题解

public class Test48{
    public static void main(String[] args) {
        //    枚举生日举办次数
        for (int i = 1; i < 100; ++i) {
            int t=i*(i-1)/2;
            if((236-t)%i==0) {
//            输出首项
                System.out.println((236-t)/i+" "+i);
            }
        }
    }
}

3、注意分数运算就手动好好算,不要写的太草率局可以了

     B      DEF
A + --- + ------- = 10
     C      GHI

static void check(int arr[]){
        int x = arr[3] * 100 + arr[4] * 10 + arr[5];
        int y = arr[6] * 100 + arr[7] * 10 + arr[8];
        if((arr[1] * y + arr[2] * x) % (y * arr[2]) == 0 && arr[0] + (arr[1] * y + arr[2] * x) / (y * arr[2]) == 10){
            count++;
        }
    }

4、

/*
分小组

9名运动员参加比赛,需要分3组进行预赛。
有哪些分组的方案呢?

我们标记运动员为 A,B,C,... I
下面的程序列出了所有的分组方法。

该程序的正常输出为:
ABC DEF GHI
ABC DEG FHI
ABC DEH FGI
ABC DEI FGH
ABC DFG EHI
ABC DFH EGI
ABC DFI EGH
ABC DGH EFI
ABC DGI EFH
ABC DHI EFG
ABC EFG DHI
ABC EFH DGI
ABC EFI DGH
ABC EGH DFI
ABC EGI DFH
ABC EHI DFG
ABC FGH DEI
ABC FGI DEH
ABC FHI DEG
ABC GHI DEF
ABD CEF GHI
ABD CEG FHI
ABD CEH FGI
ABD CEI FGH
ABD CFG EHI
ABD CFH EGI
ABD CFI EGH
ABD CGH EFI
ABD CGI EFH
ABD CHI EFG
ABD EFG CHI
..... (以下省略,总共560行)。

public class A
{
    public static String remain(int[] a)
    {
        String s = "";
        for(int i=0; i<a.length; i++){
            if(a[i] == 0) s += (char)(i+'A');
        }
        return s;
    }

    public static void f(String s, int[] a)
    {
        for(int i=0; i<a.length; i++){
            if(a[i]==1) continue;
            a[i] = 1;
            for(int j=i+1; j<a.length; j++){
                if(a[j]==1) continue;
                a[j]=1;
                for(int k=j+1; k<a.length; k++){
                    if(a[k]==1) continue;
                    a[k]=1;
                    System.out.println(__________________________________);  //填空位置
                    a[k]=0;
                }
                a[j]=0;
            }
            a[i] = 0;
        }
    }

    public static void main(String[] args)
    {
        int[] a = new int[9];
        a[0] = 1;

        for(int b=1; b<a.length; b++){
            a[b] = 1;
            for(int c=b+1; c<a.length; c++){
                a[c] = 1;
                String s = "A" + (char)(b+'A') + (char)(c+'A');
                f(s,a);
                a[c] = 0;
            }
            a[b] = 0;
        }
    }
}

仔细阅读代码,填写划线部分缺少的内容。注意:不要填写任何已有内容或说明性文字。
*/

public class _04_分小组 {
  public static String remain(int[] a)
  {
    String s = "";
    for(int i=0; i<a.length; i++){
      if(a[i] == 0) s += (char)(i+'A');
    }
    return s;
  }
static int cnt;
  public static void f(String s, int[] a)
  {
    for(int i=0; i<a.length; i++){
      if(a[i]==1) continue;
      a[i] = 1;
      for(int j=i+1; j<a.length; j++){
        if(a[j]==1) continue;
        a[j]=1;
        for(int k=j+1; k<a.length; k++){
          if(a[k]==1) continue;
          a[k]=1;
          // System.out.println(____________);  //填空位置
          System.out.println(s+(char)(i+'A') + (char)(j+'A')+(char)(k+'A')+remain(a) );
          cnt++;
          a[k]=0;
        }
        a[j]=0;
      }
      a[i] = 0;
    }
  }

  public static void main(String[] args)
  {
    int[] a = new int[9];
    a[0] = 1;

    for(int b=1; b<a.length; b++){
      a[b] = 1;
      for(int c=b+1; c<a.length; c++){
        a[c] = 1;
        String s = "A" + (char)(b+'A') + (char)(c+'A');
        f(s,a);
        a[c] = 0;
      }
      a[b] = 0;
    }
    System.out.println(cnt);
  }
}

?5、 

抽签

X星球要派出一个5人组成的观察团前往W星。
其中:
A国最多可以派出4人。
B国最多可以派出2人。
C国最多可以派出2人。
....

那么最终派往W星的观察团会有多少种国别的不同组合呢?

下面的程序解决了这个问题。
数组a[] 中既是每个国家可以派出的最多的名额。
程序执行结果为:
DEFFF
CEFFF
CDFFF
CDEFF
CCFFF
CCEFF
CCDFF
CCDEF
BEFFF
BDFFF
BDEFF
BCFFF
BCEFF
BCDFF
BCDEF
....
(以下省略,总共101行)


public class A
{
    public static void f(int[] a, int k, int n, String s)
    {
        if(k==a.length){
            if(n==0) System.out.println(s);
            return;
        }

        String s2 = s;
        for(int i=0; i<=a[k]; i++){
            _____________________________;   //填空位置
            s2 += (char)(k+'A');
        }
    }

    public static void main(String[] args)
    {
        int[] a = {4,2,2,1,1,3};

        f(a,0,5,"");
    }
}


仔细阅读代码,填写划线部分缺少的内容。

注意:不要填写任何已有内容或说明性文字。

思路,???,我想写for循环啊,写出了一个k++,因为能感觉到k是游标。一定是一个递归啊,我都看到递归的出口了呀,所以直接写一个递归函数就OK了

public class Test48
{
    public static void f(int[] a, int k, int n, String s)
    {
        if(k==a.length){
            if(n==0) System.out.println(s);
            return;
        }

        String s2 = s;
        for(int i=0; i<=a[k]; i++){
            f(a,k+1,n-i,s2);   //填空位置
            s2 += (char)(k+'A');
        }
    }

    public static void main(String[] args)
    {
        int[] a = {4,2,2,1,1,3};

        f(a,0,5,"");
    }
}

?6、

方格填数

如下的10个格子
   +--+--+--+
   |  |  |  |
+--+--+--+--+
|  |  |  |  |
+--+--+--+--+
|  |  |  |
+--+--+--+

(如果显示有问题,也可以参看【图1.jpg】)

填入0~9的数字。要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)

一共有多少种可能的填数方案?

请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

思路:应该是递归,nonono,才不是递归,,我总以为少了两个空就要递归去搜索,不用啊,直接全排列,然后手动去一个格子一个格子验证就好了呀,有重复,所以也不会很多。

之前我的代码,,这个没有办法去判断相邻不差1啊,

这道题,,艾玛,终于知道直接的abs是怎么用的了,头文件加

import static java.lang.Math.*;  这样下面的abs就不用再加Math.了
class test6{
    static int ans = 0;
    static boolean vis[][] = new boolean[3][4];
    static int a[] ={0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    public static void main(String[] args) {
        int[][] table = new int[3][4];
//        table[0][0] = Math.;//额,大数怎么赋值又忘记了,但是这个应该不需要
//        table[2][3] = ;
        vis[0][0]  = true;
        vis[2][3] = true;
        ans =dfs(0,0);
        System.out.println(ans);
    }
    static int dfs(int x,int y){
        if(x == 2 && y ==3) ans++;

        if(x-1>0 && vis[x-1][y] == false){
            vis[x-1][y] =true;
            dfs(x-1,y);
        }
        if(x+1<3 && vis[x+1][y] == false){
            vis[x+1][y] = true;
            dfs(x+1,y);
        }
        if(y-1>0 && vis[x][y-1] == false){
            vis[x][y-1] = true;
            dfs(x,y-1);
        }
        if(y+1<4 && vis[x][y+1] == false){
            vis[x][y+1] = true;
            dfs(x,y+1);
        }

        return ans;
    }

}

改后代码,,还是一直都是0 ,答案应是1580,这里涨一下记性啦,一直是0可能是因为我某个if条件判断的括号有问题,一不小心扩的多了,就跳过了,,要会用debug啊,跳过了那个函数就是因为if不满足才跳过的

class test6{
    static int ans = 0;
    static boolean vis[][] = new boolean[3][4];
    static int a[] ={0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    public static void main(String[] args) {

        dfs(0);
        System.out.println(ans);
    }
    static void dfs(int k){
        if(k==10) {
            boolean b = check();
            if(b){
                ans++;//这里如突然
                return;
            }

            for(int i =k;i<10;i++){
                int tmp = a[i];
                a[i] = a[k];
                a[k] =tmp;

                dfs(k+1);

                tmp = a[i];
                a[i] = a[k];
                a[k] = tmp;
            }
        }
    }

    static boolean check(){
        if (Math.abs(a[0] - a[1]) == 1 ||
                Math.abs(a[0] - a[3]) == 1 ||
                Math.abs(a[0] - a[4]) == 1 ||
                Math.abs(a[0] - a[5]) == 1 ||

                Math.abs(a[1] - a[2]) == 1 ||
                Math.abs(a[1] - a[4]) == 1 ||
                Math.abs(a[1] - a[5]) == 1 ||
                Math.abs(a[1] - a[6]) == 1 ||

                Math.abs(a[2] - a[5]) == 1 ||
                Math.abs(a[2] - a[6]) == 1 ||

                Math.abs(a[3] - a[4]) == 1 ||
                Math.abs(a[3] - a[7]) == 1 ||
                Math.abs(a[3] - a[8]) == 1 ||

                Math.abs(a[4] - a[5]) == 1 ||
                Math.abs(a[4] - a[7]) == 1 ||
                Math.abs(a[4] - a[8]) == 1 ||
                Math.abs(a[4] - a[9]) == 1 ||

                Math.abs(a[5] - a[6]) == 1 ||
                Math.abs(a[5] - a[8]) == 1 ||
                Math.abs(a[5] - a[9]) == 1 ||

                Math.abs(a[6] - a[9]) == 1 ||

                Math.abs(a[7] - a[8]) == 1 ||

                Math.abs(a[8] - a[9]) == 1)
            return false;
        return true;
    }

}

正确代码

import static java.lang.Math.abs;

public class test6 {

    static int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    static int ans;

    static boolean check() {
        if (abs(a[0] - a[1]) == 1 ||
                abs(a[0] - a[3]) == 1 ||
                abs(a[0] - a[4]) == 1 ||
                abs(a[0] - a[5]) == 1 ||

                abs(a[1] - a[2]) == 1 ||
                abs(a[1] - a[4]) == 1 ||
                abs(a[1] - a[5]) == 1 ||
                abs(a[1] - a[6]) == 1 ||

                abs(a[2] - a[5]) == 1 ||
                abs(a[2] - a[6]) == 1 ||

                abs(a[3] - a[4]) == 1 ||
                abs(a[3] - a[7]) == 1 ||
                abs(a[3] - a[8]) == 1 ||

                abs(a[4] - a[5]) == 1 ||
                abs(a[4] - a[7]) == 1 ||
                abs(a[4] - a[8]) == 1 ||
                abs(a[4] - a[9]) == 1 ||

                abs(a[5] - a[6]) == 1 ||
                abs(a[5] - a[8]) == 1 ||
                abs(a[5] - a[9]) == 1 ||

                abs(a[6] - a[9]) == 1 ||

                abs(a[7] - a[8]) == 1 ||

                abs(a[8] - a[9]) == 1)
            return false;
        return true;
    }

    /*考虑第k个位置,一般从0开始*/
    static void f(int k) {
//出口
        if (k == 10) {
            boolean b = check();
            if (b)
                ans++;
            return;
        }


        for (int i = k; i < 10; ++i) {
            //尝试将位置i与位置k交换,以此确定k位的值
            {
                int t = a[i];
                a[i] = a[k];
                a[k] = t;
            }
            f(k + 1);
//        回溯
            {
                int t = a[i];
                a[i] = a[k];
                a[k] = t;
            }

        }
    }

    public static void main(String[] args) {
        f(0);
        System.out.println(ans);
    }
}

7、

剪邮票

如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。

请你计算,一共有多少种不同的剪取方法。

请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

我的代码:这么算来答案只有1,,不可能的,我用的是递归,但是递归不是不对,是我不会写哎,递归不对的地方在于协斜联通不叫联通,递归就不能包含递归的那种方法,,,官方题解,卧草,全排列,然后检查连通性!!!

看了好多代码,大家都是全排列+递归检测连通性,,,不明白为什么只用递归不可以???、

全排列,在12个里面挑出5个一堆的全排列来,但是全排列也有不同的方法呀,可以用我写的最多的递归,来回换的那个,但是这个是12个0里面5个1,那12个0就会有很大的重复的可能性,所以还需要解决的就是重复的问题,一种办法就是用前面那个办法,再加个set维护一下,另一种办法就是去递归,但是需要一个条件去维护一下,,,敲黑板!!模板来了!!注意递归;里面的那个维护。另外还有一个模板,,二维矩阵dfs判断连通性!!!经典代码

如下是我原来的代码:输出一直是1,好像删掉T形的可能性,也没有这么少吧

class Test48{
    static int a[][] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}};
    static boolean vis[][]= new boolean[3][4];
    static int ans = 0;
    public static void main(String[] args) {
        dfs(0,0,0);
        System.out.println(ans);
    }
    static void dfs(int x,int y,int count){
        if(count ==5) ans++;

        for(int i =0;i<3;i++){
            for(int j = 0;j<4;j++){
                if(i-1>0 && vis[i-1][j]==false){
                    vis[i-1][j] =true;
                    dfs(i-1,j,count+1);
                }
                if(i+1<3 && vis[i+1][j] ==false){
                    vis[i+1][j] =true;
                    dfs(i+1,j,count+1);
                }
                if(j-1>0 && vis[i][j-1] ==false){
                    vis[i][j-1] =true;
                    dfs(i,j-1,count+1);
                }
                if(j+1<4 && vis[i][j+1] ==false){
                    vis[i][j+1] =true;
                    dfs(i,j+1,count+1);
                }
            }
        }
    }
}

 官方答案:

class Tets48{
    static int a[] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};//它的每个排列代表着12选5的一个方案
    static int ans;

    public static void main(String[] args) {
        int path[] = new int[12];
        f(0,path);
        System.out.println(ans);
    }
    static boolean vis[] = new boolean[12];

    static void f(int k,int[] path){
        if(k==12){
            if(check(path)){
                ans++;
            }
        }
        for(int i =0;i<12;i++){
            if(i>0 && a[i]==a[i-1] && !vis[i-1]) continue;//现在准备选取的元素和上一个元素相同,但是上一个元素还没被使用!!!维护不重复

            if(!vis[i]){
                vis[i] = true;
                path[k] = a[i];
                f(k+1,path);//最后要留下一个哪些位置是1啊,需要一个path记录一下
                vis[i] =false;//回溯
            }
        }
    }

    static boolean check(int [] path){//dfs判断连通性
        int g[][] = new int[3][4];
        //将排列映射到二维矩阵上
        for(int i =0;i<3;i++){
            for(int j =0;j<4;j++){
                if(path[i*4+j]==1) g[i][j] =1;
                else  g[i][j] =  0;
            }
        }

        int cnt = 0; //连通块的数目

        for(int i =0;i<3;i++){
            for(int j = 0;j<4;j++){
                if(g[i][j]==1) {
                    dfs(g,i,j);
                    cnt++;
                }
            }
        }
        return cnt==1;
    }

    static void dfs(int g[][],int i,int j){
        g[i][j] = 0;
        if (i - 1 >= 0 && g[i - 1][j] == 1) dfs(g, i - 1, j);
        if (i + 1 <= 2 && g[i + 1][j] == 1) dfs(g, i + 1, j);
        if (j - 1 >= 0 && g[i][j - 1] == 1) dfs(g, i, j - 1);
        if (j + 1 <= 3 && g[i][j + 1] == 1) dfs(g, i, j + 1);
    }
}

?8、

四平方和

四平方和定理,又称为拉格朗日定理:
每个正整数都可以表示为至多4个正整数的平方和。
如果把0包括进去,就正好可以表示为4个数的平方和。

比如:
5 = 0^2 + 0^2 + 1^2 + 2^2
7 = 1^2 + 1^2 + 1^2 + 2^2
(^符号表示乘方的意思)

对于一个给定的正整数,可能存在多种平方和的表示法。
要求你对4个数排序:
0 <= a <= b <= c <= d
并对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法


程序输入为一个正整数N (N<5000000)
要求输出4个非负整数,按从小到大排序,中间用空格分开

例如,输入:
5
则程序应该输出:
0 0 1 2

再例如,输入:
12
则程序应该输出:
0 2 2 2

再例如,输入:
773535
则程序应该输出:
1 1 267 838

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 3000ms
 

我的代码:根本不知道怎么写嘛,到那时感觉像是循环里套递归的感觉

解题思路偏暴力,暴力如何省时,一是减枚举的范围,二是减变量,尽量用一个变量去表示另一个变量

可是这样还是会超时,那我们反过来做,先去枚举c^2+d^2 然后把他缓存起来,然后用N-(a^2+b^2)然后去用哈希表查 那个减出来的值是否是两个数的平方和,

我原来的代码,,不知道在写啥

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Test48
{
    static int n;
    static List list = new ArrayList();
    public static void main(String[] args)
    {
        Scanner sc = new Scanner(System.in);
        n  = sc.nextInt();
        f(n,list);
    }
    public static void f(int n,ArrayList list)
    {
        int sum = 0;
        int liu  = n;
        for(int i = 0;i<4;i++){
            for(int j = 0;j*j<n;j++){
               sum+=j*j;
               list.add(j);
               f(n-j*j,list);
            }
        }
    }


}

官方题解

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

class Test48{
    static int N;
    static Map<Integer,Integer> map = new HashMap<>();
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt();
        for(int c =0;c<N/2;c++){
            for(int d =0;c*c+d*d<=N;d++){
                if(map.get(c*c+d*d)==null){
                    map.put(c*c+d*d,c);
                }
            }
        }
        for(int a =0;a<N/4;a++){
            for(int b = 0;a*a+b*b<N;b++){
                if(map.get(N-a*a-b*b)!=null){
                    int c = map.get(N-a*a-b*b);
                    int d = (int)Math.sqrt(N-a*a-b*b-c*c);
                    System.out.printf("%d %d %d %d\n",a,b,c,d);
                    break;//或者用return都可以
                }
            }
        }
    }
}

 

?9、

取球博弈

两个人玩取球的游戏。
一共有N个球,每人轮流取球,每次可取集合{n1,n2,n3}中的任何一个数目。
如果无法继续取球,则游戏结束。
此时,持有奇数个球的一方获胜。
如果两人都是奇数,则为平局。

假设双方都采用最聪明的取法,
第一个取球的人一定能赢吗?
试编程解决这个问题。

输入格式:
第一行3个正整数n1 n2 n3,空格分开,表示每次可取的数目 (0<n1,n2,n3<100)
第二行5个正整数x1 x2 ... x5,空格分开,表示5局的初始球数(0<xi<1000)

输出格式:
一行5个字符,空格分开。分别表示每局先取球的人能否获胜。
能获胜则输出+,
次之,如有办法逼平对手,输出0,
无论如何都会输,则输出-

例如,输入:
1 2 3
1 2 3 4 5

程序应该输出:
+ 0 + 0 -

再例如,输入:
1 4 5
10 11 12 13 15

程序应该输出:
0 - 0 + +

再例如,输入:
2 3 5
7 8 9 10 11

程序应该输出:
+ 0 0 0 0


资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 3000ms

开始代码

import java.util.Arrays;
import java.util.Scanner;

class Test48{
    static int[] n = new int[3];
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        for (int i = 0; i < 3; i++) {
            n[i] = sc.nextInt();
        }
        Arrays.sort(n);//排序
        for(int i=0;i<5;i++){
            int num = sc.nextInt();
            char res = f(num,0,0);
            System.out.print(res+" ");
        }
        System.out.println();
    }
    static char f(int num,int me,int you){

        if(num<n[0]){
            if((me&1)==1&&(you&1)==0)return '+';
            else if((me&1)==0&&(you&1)==1)return '-';
            else return '0';
        }
        boolean ping = false;
        for( int i=0;i<3;i++){
            if(num>=n[i]){
                char res = f(num-n[i],you,me+n[i]);
                if(res == '-'){//对方输了
                    return '+';
                }
                if(res=='0'){
                    ping =true;
                }
            }
        }
         if(ping){
             return '0';
         }
         else {
             return '-';
         }
    }

}

这个代码不太好是因为如果一共有1000多个求,而取的时候只能一个一个取的话,递归就太深了,所以不能这样,可以优化

import java.util.Arrays;
import java.util.Scanner;

class Test48{
    static int[] n = new int[3];
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        for (int i = 0; i < 3; i++) {
            n[i] = sc.nextInt();
        }
        Arrays.sort(n);//排序
        for(int i=0;i<5;i++){
            int num = sc.nextInt();
            char res = f(num,0,0);
            System.out.print(res+" ");
        }
        System.out.println();
    }

    static char[][][]cache = new char[1000][2][2];
    static char f(int num,int me,int you){

        if(num<n[0]){
            if((me&1)==1&&(you&1)==0)return '+';
            else if((me&1)==0&&(you&1)==1)return '-';
            else return '0';
        }

        if(cache[num][me][you]!='\0') return cache[num][me][you];
        boolean ping = false;
        for( int i=0;i<3;i++){
            if(num>=n[i]){
                char res = f(num-n[i],you,(n[i]&1)==0?me:(1-me));//注意此处,传递me和you的奇偶性,这儿我不太懂,1-me???难道攒的是0和1??
                if(res == '-'){//对方输了
                    cache[num][me][you]='+';
                    return '+';
                }
                if(res=='0'){
                    ping =true;
                }
            }
        }
         if(ping){
             cache[num][me][you]='0';
             return '0';
         }
         else {
             cache[num][me][you]='-';
             return '-';
         }
    }

}

 10、

压缩变换

小明最近在研究压缩算法。
他知道,压缩的时候如果能够使得数值很小,就能通过熵编码得到较高的压缩比。
然而,要使数值很小是一个挑战。

最近,小明需要压缩一些正整数的序列,这些序列的特点是,后面出现的数字很大可能是刚出现过不久的数字。对于这种特殊的序列,小明准备对序列做一个变换来减小数字的值。

变换的过程如下:
从左到右枚举序列,每枚举到一个数字,如果这个数字没有出现过,则将数字变换成它的相反数,如果数字出现过,则看它在原序列中最后的一次出现后面(且在当前数前面)出现了几种数字,用这个种类数替换原来的数字。

比如,序列(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,表示序列的长度。
第二行包含n个正整数,表示输入序列。

输出格式:
输出一行,包含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.*;

class Test48{
    static int n;
    static int[] a;
    static int[] res;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        a = new int[n];
        res = new int[n];
        for(int i=0;i<n;i++){
            a[i] = sc.nextInt();
        }
        Map<Integer,Integer> map = new HashMap<>();
        for(int i=0;i<n;i++){
            if(map.get(a[i])==null){
                res[i] = -a[i];
                map.put(a[i],i);
            }
            else {
                int x = map.get(a[i]);
               res[i] = check(a,x,i);
                map.put(a[i],i);
            }
        }
        for(int i =0;i<n;i++){
            System.out.print(res[i]+" ");
        }
    }
    static int check(int[] a,int x,int i){
        Set set = new HashSet();
        for(int j =x+1;j<i;j++){
            set.add(a[j]);
        }
        return set.size();
    }
}

这样只能有30分,在区间内查找有多少种数字的那里可以有优化,set还是太慢了

顺便总计一下

区间和查询,以下方法一个比一个更高级

方法一:前缀和,

但是前缀和只适用于静态数据,公式 sum【i】 = 西格玛,j =0到i,a【i】;(sum【i】是指从0到i的和,这样做呢,要求一个区间,比如m到n,那它就是sum【n】减sum【m】就可以啦)为啥快呢,因为他是线性时间,只需要查一下n的地方,查一下m的地方就OK啦,那为啥呢必须是静态数据呢,因为这种情况下,数组中改一个地方的值,剩下的所有地方的值就都要改了,查询时o(1),但是修改会导致o(n);

方法二:树状数组,

大概是倍增的一个思路,有的节点归上面别的管,有的自己管,然后从下面一直到上面,最后汇聚成一个节点管着,查询和修改都是log(n),求区间和也是从上面往下面加几个就可以了,,可以去查一下图长什么样子就好理解一点了。这个方法比较男理解,但是代码比较简单

方法三:区间树(又叫线段树)

,它既可以求区间的最值又可以求区间的和(搜一下灾后重建那个题),他的查询和修改都只是logn,因为每次都只是干扰和涉及一半的区间,是那么一半一半的上升和下降的,

 

那么,用区间树做这个题的思路:用0和1来存,把最后一次这个数出现的位置存为1,(之前有他的地方又换回0)然后看开始和结束节点之间有几个1,那就是不同的数字有几个

我的代码如下,思路没有问题,但是哪里可能小小的写错了一下,答案就飘了

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

class Test48{
    static int n;
    static int[] a;
    static int[] res;
    static int[] b;//这是一个0 1 序列,某一个位置是1,代表a【p】这个数字最后一次出现的位置是p
    static SegTree root;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        a = new int[n];
        res = new int[n];
        b = new int[n];
        root = buildSegTree(0,n-1);//把这颗树建立起来
        for(int i=0;i<n;i++){
            a[i] = sc.nextInt();
        }
       Map<Integer,Integer> map = new HashMap<>();
        for(int i=0;i<n;i++){
            if(map.get(a[i])==null){
                res[i] = -a[i];
                b[i] = 1;
              map.put(a[i],i);
                update(root, i, 1);
            }
            else {
                int x = map.get(a[i]);
               res[i] = query(root,x+1,i-1);//统计两个位置之间的1的个数
                b[x] = 0;
                b[i] = 1;
              map.put(a[i],i);
                update(root, map.get(a[i]), -1);
                update(root, i, 1);
            }
        }
        for(int i =0;i<n;i++){
            System.out.print(res[i]+" ");
        }
    }
    static int query(SegTree tree,int x,int y){//这个起始以前写过,就是查询包含左右边界直接返回,不然的话去更新左右
        int l = tree.l;
        int r = tree.r;
        if(x<=l && y>=r){//不要担心这里上来就全包含了就少了,因为不可能上来查询的区间就大于整体所有的区间的
            return tree.sum;
        }
        int mid = (l+r)/2;
        int ans  = 0;
        if(x<=mid) ans+=query(tree.lson,x,y);
        if(y>mid) ans+=query(tree.rson,x,y);
        return ans;
    }

    static void update(SegTree tree,int p,int i){//i是增量的意思
        if(tree == null){//多走了一层,就是没有了,其实是一样的
            return;
        }
        //更新根节点的sum
        tree.sum+=i;
        int l = tree.l;
        int r = tree.r;
        int mid = (l+r)>>1;
        if(p<=mid){
            update(tree.lson,p,i);
        }
        else {
            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;
        }
    }

    static SegTree buildSegTree(int l,int r){
        SegTree segTree = new SegTree(l,r);
        if(l==r){ //这是构建这颗树时,左等于有就是到了叶子节点了
            segTree.sum = b[l];
            return segTree;
        }
        int mid = (l+r)/2;
        SegTree lson  = buildSegTree(l,mid);
        SegTree rson = buildSegTree(mid+1,r);

        segTree.lson = lson;
        segTree.rson = rson;
     //   segTree.sum = Math.max(lson.sum,rson.sum);//所以这种树的模板灵活在哪里呢,如果想求区间最值的话就用区间和用max,如果想求区间和的话就
        segTree.sum=lson.sum+rson.sum;

        return segTree;
    }
}

标准代码,有时间一定要检查一下代码呀

public class _10_压缩变换_2 {
  static Map<Integer, Integer> lastIndex = new HashMap<Integer, Integer>();//数字与下标的映射
  static int[] a;//记录原始数据
  static int[] ans;//记录答案
  static int[] b;//这是一个01序列,某一个位置p上的数字为1,代表着a[p]这个数字最后出现的位置是p,而a[p]曾经出现过的位置上都是0
  private static int n;
  private static SegTree root;

  public static void main(String[] args) throws FileNotFoundException {
    System.setIn(new FileInputStream(new File("/Users/zhengwei/workspace/lanqiaobei2019/src/2016_Java_A/data10/in8.txt")));
    System.setOut(new PrintStream(new File("/Users/zhengwei/workspace/lanqiaobei2019/src/2016_Java_A/data10/myout8.txt")));
    //  处理输入
    Scanner sc = new Scanner(System.in);
    n = sc.nextInt();
    a = new int[n];
    ans = new int[n];
    b=new int[n];
    root = buildSegTree(0, n - 1);

    for (int i = 0; i < n; i++) {
      int num = sc.nextInt();
      a[i] = num;
      Integer preIndex = lastIndex.get(num);
      if (preIndex == null)//没出现过
      {
        ans[i] = -num;
        b[i] = 1;
        update(root, i, 1);
      } else {
        //统计p_num和i之间有多少不同的数字
        ans[i] = query(root, preIndex + 1, i - 1);//统计两个位置之间的1的个数==>求区间和
        b[preIndex] = 0;
        b[i] = 1;
        update(root, preIndex, -1);
        update(root, i, 1);
      }
      lastIndex.put(num, i);//更新
    }
    for (int i = 0; i < n; i++) {
      System.out.print(ans[i] + " ");
    }
  }

  private static int query(SegTree tree, int x, int y) {
    int l = tree.l;
    int r = tree.r;
    if (x <= l && y >= r) return tree.sum;
    int mid = (l + r) / 2;
    int ans = 0;
    if (x <= mid) ans += query(tree.lson, x, y);
    if (y > mid) ans += query(tree.rson, x, y);
    return ans;
  }

  /*构建线段树*/
  private static SegTree buildSegTree(int l, int r) {
    SegTree segTree = new SegTree(l, r);
    if (l == r) {
      segTree.sum = b[l];
      return segTree;
    }
    int mid = (l + r) / 2;
    SegTree lson = buildSegTree(l, mid);
    SegTree rson = buildSegTree(mid + 1, r);
    segTree.lson = lson;
    segTree.rson = rson;
    segTree.sum = lson.sum + rson.sum;
    return segTree;
  }

  static void update(SegTree tree, int p, int i) {
    if (tree == null) return;
    //更新根节点的sum
    tree.sum += i;
    int l = tree.l;
    int r = tree.r;
    int mid = (l + r) >> 1;
    if (p <= mid) {
      update(tree.lson, p, i);
    } else {
      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;
    }
  }
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本文档为2016本人参加全国大学生数学建模参赛最后提交文档(文档中最后附录包含matlab代码) 摘要 小区开放是当今热议的缓解小区周边道路交通堵塞的方法之一,本文在一定假设的前提下,通过建合适的评价体系和数学模型,进行计机仿真,得到定量的数据结论,对比分析不同小区在进行小区开放后,对周边道路的通行影响。 针对问题一,通过文献查找,获取相关的道路通行评价指标,结合小区周边实际情况,运用BP神经网络,得到一套合适的评价体系(道路交通运行指数,道路交通拥堵率,平均行程速度,平均延误时间)。 针对问题二,使用元胞自动机和网格化图,建立与现实情况相符合的静态建筑物道路参数和动态车辆通行模型,并考虑司机是否具有获得前方道路信息的能力,分别建立基于排队论思想和基于道路阻抗系数的路径选择策略模型。 针对问题三,将不同的小区类型进行合理抽象,得到基本典型结构。结合由问题二得到的模型进行建模仿真,将得到的结果按照问题一得到的评价体系进行评价,并进行可视化和数据分析得到小区开放在一定程度上可以缓解小区周边道路交通压力。 针对问题四,根据问题三得到的结论,通过控制变量法对比各个条件下车流通行的情况,得出有利条件与不利条件。提出合理的建议,并以简单书信形式表述。 关键词:小区开放、BP神经网络、元胞自动机、动态建模
2000网易杯数学建模竞赛的A题是关于水泥生产的问题。题目中提到有两种生产方式,一种是连续生产,另一种是间歇生产。 连续生产方式下,每个周期内生产的水泥总产量是一个固定值,但是生产周期中会有一定的浪费。题目要求我们通过建立数学模型,最大化总产量且最小化浪费。 首先,我们可以设定每个周期的生产量为x,假设有n个周期。总产量就是x乘以n。 接着,题目给出了浪费的计方法,即产量仅在下限和上限之间的部分会被作废。我们可以通过设定合适的下限和上限,来计浪费量,并将其最小化。 根据题目中的条件,我们可以把问题转化为一个优化问题。即在一定约束条件下,寻找最适合的生产周期和产量使得总产量最大化和浪费最小化。这个问题可以通过建立数学模型并使用优化法来解决。 在模型中,我们需要确定一些参数,比如生产周期的下限和上限、周期周期内的生产量取值范围等。我们可以通过对问题进行细致的分析和抽象,来确定这些参数的合理取值。 最后,我们可以使用数学建模的方法,将问题转化为一个数学表达式,并使用优化法来求解最优解。常用的优化法有线性规划、整数规划、遗传法等。 总的来说,2000网易杯数学建模竞赛的A题是一个关于水泥生产的优化问题。通过建立数学模型和使用适当的优化法,我们可以找到最优的生产周期和产量,实现总产量最大化和浪费最小化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值