Winter Vacation Training(3st Week)

最大子列和问题 (20分)

给定K个整数组成的序列{ N​1​​, N​2​​, ..., N​K​​ },“连续子列”被定义为{ N​i​​, N​i+1​​, ..., N​j​​ },其中 1≤i≤j≤K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。

本题旨在测试各种不同的算法在各种数据情况下的表现。各组测试数据特点如下:

  • 数据1:与样例等价,测试基本正确性;
  • 数据2:102个随机整数;
  • 数据3:103个随机整数;
  • 数据4:104个随机整数;
  • 数据5:105个随机整数;

输入格式:

输入第1行给出正整数K (≤100000);第2行给出K个整数,其间以空格分隔。

输出格式:

在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。

输入样例:

6
-2 11 -4 13 -5 -2

输出样例:

20

 代码:

刚开始写的太麻烦,其实很简单。。

maxsum初值为0即保证了当所有整数为负数时即任何子列小于0时结果输出0

注意1<=i<=j<=K,也就是说子列可以只为一个元素,因为全部为负数时输出0,而只要有一个元素为正数那么结果必定大于0,所以让subsum<0时再重新置为0。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class 最大子列和 {

	public static void main(String[] args) throws NumberFormatException, IOException {
		// TODO 自动生成的方法存根
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        int K=Integer.parseInt(br.readLine());
        String[] s=br.readLine().split(" ");
        int L=s.length;
        int subsum=0,maxsum=0;
        for(int i=0;i<L;i++){//直接在一条线上前进
        	subsum+=Integer.parseInt(s[i]);
        	if(maxsum<subsum){
        		maxsum=subsum;
        	}
        	if(subsum<0){
        		subsum=0;
        	}
        }
        System.out.println(maxsum);
	}

}

 一元多项式的乘法与加法运算 (20分)

设计函数分别求两个一元多项式的乘积与和。

输入格式:

输入分2行,每行分别先给出多项式非零项的个数,再以指数递降方式输入一个多项式非零项系数和指数(绝对值均为不超过1000的整数)。数字间以空格分隔。

输出格式:

输出分2行,分别以指数递降方式输出乘积多项式以及和多项式非零项的系数和指数。数字间以空格分隔,但结尾不能有多余空格。零多项式应输出0 0

输入样例:

4 3 4 -5 2  6 1  -2 0
3 5 20  -7 4  3 1

输出样例:

15 24 -25 22 30 21 -10 20 -21 8 35 6 -33 5 14 4 -15 3 18 2 -6 1
5 20 -4 4 -5 2 9 1 -2 0

思路:

模拟题,第二个点答案错误。。写起来有点复杂,注意细节,乘积时是指数相加。

代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class 一元多项式的乘法与加法运算 {

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        String[] first=br.readLine().split("\\s+");
        String[] second=br.readLine().split("\\s+");//以一个或多个空格切分
        int num1=Integer.parseInt(first[0]);
        int num2=Integer.parseInt(second[0]);
        int[][] row1=new int[1001][2];
        int[][] row2=new int[1001][2];
        for(int i=1,count=0;count<num1;i+=2,count++){
        	row1[Integer.parseInt(first[i+1])][0]=Integer.parseInt(first[i]);//第一列存储系数
        	row1[Integer.parseInt(first[i+1])][1]=1;//此坐标的指数项存在
        }
        for(int i=1,count=0;count<num2;i+=2,count++){
        	row2[Integer.parseInt(second[i+1])][0]=Integer.parseInt(second[i]);//第一列存储系数
        	row2[Integer.parseInt(second[i+1])][1]=1;//此坐标的指数项存在
        }
        //乘积多项式
        int[][] mul=new int[1000001][2];
        for(int i=0;i<1001;i++){
        	for(int j=0;j<1001;j++){
        		if(row1[i][1]==1&&row2[j][1]==1){
        			//注意乘积时指数是相
        			mul[i+j][0]+=row1[i][0]*row2[j][0];
        			mul[i+j][1]=1;
        		}
        	}
        }
        //输出
        int ff=0;
        for(int i=1000000;i>=0;i--){
        	if(mul[i][0]!=0){
        		ff=1;
        		break;
        	}
        }
        if(ff==0){//零多项式
        	System.out.println("0 0");
        }
        else{
        	int flag=0;
            for(int i=1000000;i>=0;i--){
            	
            	if(mul[i][1]==1&&flag==0){//有此指数项
            		System.out.print(mul[i][0]+" ");
            		System.out.print(i);
            		flag++;
            		continue;
            	}
            	if(mul[i][1]==1&&flag!=0){//有此指数项
            		System.out.print(" "+mul[i][0]);
            		System.out.print(" "+i);
            	}
            }
            System.out.println();
        }
        //和多项式
        int[][] add=new int[1001][2];
        for(int i=0;i<1001;i++){
        	add[i][0]=row1[i][0]+row2[i][0];
            if(row1[i][1]==1||row2[i][1]==1){
            	add[i][1]=1;
            }
        }
        //输出
        int f=0;
        for(int i=1000;i>=0;i--){
        	if(add[i][0]!=0){
        		f=1;
        		break;
        	}
        }
        if(f==0){
        	System.out.println("0 0");
        }
        else{
        	int flag1=0;
            for(int i=1000;i>=0;i--){
            	
            	if(add[i][1]==1&&flag1==0){//有此指数项
            		System.out.print(add[i][0]+" ");
            		System.out.print(i);
            		flag1++;
            		continue;
            	}
            	if(add[i][1]==1&&flag1!=0){//有此指数项
            		System.out.print(" "+add[i][0]);
            		System.out.print(" "+i);
            	}
            }
            System.out.println();
        }
	}

}

阶乘之和取模 (25分)

输入正整数n, 计算S = 1!+2!+...+n!的末6位(不含前导0). 这里1<=n<=10​9​​.

输入样例:

例如输入:

20

输出样例:

输出:

820313

思路:

之前做过,一看这个数据量就得用大数类(注,BigInteger是整数大数类,BigDecimal是浮点数大数类,用法都差不多),

这道题体现了打表的重要性,后两个测试点内存超限,打表一看n>=24时后六位都为940313,直接if将后面的一锅端。。

打表找规律(前100个):

1
3
9
33
153
873
5913
46233
409113
37913
954713
956313
977113
268313
636313
524313
620313
348313
180313
*****820313*****
260313
940313
580313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313
940313

 法一:BigInteger(粗暴)

import java.math.BigInteger;
import java.util.Scanner;
public class 阶乘之和取模 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
        //斯特林公式
		Scanner in=new Scanner(System.in);
		int n=in.nextInt();
		if(n>=24){
			System.out.println(940313);
			return;
		}
		BigInteger num=new BigInteger("0");
		BigInteger rn=new BigInteger("1000000");
		for(int i=1;i<=n;i++){
			BigInteger tmp=new BigInteger("1");
			for(int j=1;j<=i;j++){
				BigInteger jj=new BigInteger(String.valueOf(j));
				tmp=tmp.multiply(jj);
			}
			num=num.add(tmp);
		}
		System.out.println(num.mod(rn));
	}

}

法二:略有巧妙,转化到O(n)

import java.util.Scanner;
public class 阶乘之和取模法二 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
        Scanner in=new Scanner(System.in);
        int n=in.nextInt();
        if(n>=24){
        	System.out.println(940313);
        	return;
        }
        int max=0,tmp=1;
        for(int i=1;i<=n;i++){
        	tmp*=i;
        	tmp%=1000000;
        	max+=tmp;
        	max%=1000000;
        }
        System.out.println(max%1000000);
	}

}

王牌特工3 (15分)

众所周知旺仔是来自阿联酋的富二代,但是其实这一切都是假的!他的真实身份是火星情报局的王牌特工,这一切的一切都起源与一个绝密的策划......(此处省略1万字),一天晚上,旺仔特工准备向火星情报局发送一个十万火急的文件,但是怕文件泄露,所以他决定对文件进行加密,加密规则如下:

首先,在信的开头会有一个“加密信息”,接下来是正文(当然是加密过的火星文),要通过一定的规则把火星文解密,每个火星文字符都会变成一个对应的火星文字符。 如果“加密信息”是“ACM”,则加密规则如下: A C M B D E F G H I J K L N O P Q R S T U V W X Y Z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 你可能会发现在“加密信息”——“ACM”排在了最前面,26个火星文字符除此之外依次排列在其后。于是每个字符形成了一一对应的关系。如上对应‘A’->‘A’,‘C’->‘B’,‘M’->‘C’,‘B’->‘D’,以此类推。小写字符也相同‘a->‘a,‘c->‘b’,‘m->‘c’,‘b’->‘d’。

输入格式:

多组输入。第一行输入一个字符串,表示“加密信息”(全部为大写英文字符,且任意两个字符不相同,比如“AAB”)。第二行输入一个整数n(1<=n<=10)代表正文的行数。接下来n行,每行输入一个字符串,表示一行正文(字符串长度小于200)。

输出格式:

除加密信息外,输出n行,每行是被解密的正文。

输入样例:

ACM
2
ee ff gg jj kk aa bb cc mm dd
H al wanf zah
WCM
1
FW Fw

输出样例:

在这里给出相应的输出。例如:

ff gg hh kk ll aa dd bb cc ee
I am wang zai
HA Ha

思路:

先理清大体思路,写一个fun函数专门进行a~z的数组转换成密钥数组,设一个小写和大写的数组,每次都进行密钥的转换,然后每一行输入的密文都逐个字符的分情况讨论变换输出就ok了。

因为需要区分大写小写,所以直接用char类型,方便区分。主要的坑点就在于解密过程,将指定的字母全部移动到前面去。在这一过程中千万要注意找指定字母的时候遍历数组找,而不要想当然的直接字母ASCII值转换成下标,因为每次有些字母的位置(下标)都会改变!!!

 代码:

import java.util.Scanner;
public class 王牌特工 {
	static void fun(char[][] a,String str){//求私钥
		//大写
		for(int i=str.length()-1;i>=0;i--){
			char tmp=str.charAt(i);
			///前面所有元素依次往前移动一位,是找字母位置而不是找坐标!!!
			int loc=0;
			for(int k=0;k<26;k++){
				if(a[1][k]==tmp){
					loc=k;
					break;
				}
			}
			for(int j=loc;j>=1;j--){
				a[1][j]=a[1][j-1];
			}
			a[1][0]=tmp;
		}
		//小写
		str=str.toLowerCase();
		for(int i=str.length()-1;i>=0;i--){
			char tmp=str.charAt(i);
			int loc=0;
			for(int k=0;k<26;k++){
				if(a[0][k]==tmp){
					loc=k;
					break;
				}
			}
			for(int j=loc;j>=1;j--){
				a[0][j]=a[0][j-1];
			}
			a[0][0]=tmp;
			//System.out.println(Arrays.toString(a[1]));
		}
	}

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
        Scanner in=new Scanner(System.in);
        while(true){
        	String key=in.nextLine();
        	//求私钥
        	char[][] letter=new char[2][26];
        	for(int i=0;i<26;i++){
        		letter[0][i]=(char)('a'+i);
        		letter[1][i]=(char)('A'+i);
        	}
        	fun(letter,key);
        	//解密
        	int n=in.nextInt();
        	String ag=in.nextLine();//读取换行
        	for(int i=0;i<n;i++){
        		String secret=in.nextLine();
        		int L=secret.length();
        		for(int j=0;j<L;j++){
        			char tmp=secret.charAt(j);
        			if(tmp>=65&&tmp<=90){//大写
        				for(int k=0;k<26;k++){
        					if(tmp==letter[1][k]){
        						System.out.print((char)('A'+k));
        						break;
        					}
        				}
        			}
        			else if(tmp>=97&&tmp<=122){//小写
        				for(int k=0;k<26;k++){
        					if(tmp==letter[0][k]){
        						System.out.print((char)('a'+k));
        						break;
        					}
        				}
        			}
        			else{//其他字符原样输出
        				System.out.print(tmp);
        			}
        		}
        		System.out.println();
        	}
        }
	}

}

彩虹瓶 (25分)

rb.JPG

彩虹瓶的制作过程(并不)是这样的:先把一大批空瓶铺放在装填场地上,然后按照一定的顺序将每种颜色的小球均匀撒到这批瓶子里。

假设彩虹瓶里要按顺序装 N 种颜色的小球(不妨将顺序就编号为 1 到 N)。现在工厂里有每种颜色的小球各一箱,工人需要一箱一箱地将小球从工厂里搬到装填场地。如果搬来的这箱小球正好是可以装填的颜色,就直接拆箱装填;如果不是,就把箱子先码放在一个临时货架上,码放的方法就是一箱一箱堆上去。当一种颜色装填完以后,先看看货架顶端的一箱是不是下一个要装填的颜色,如果是就取下来装填,否则去工厂里再搬一箱过来。

如果工厂里发货的顺序比较好,工人就可以顺利地完成装填。例如要按顺序装填 7 种颜色,工厂按照 7、6、1、3、2、5、4 这个顺序发货,则工人先拿到 7、6 两种不能装填的颜色,将其按照 7 在下、6 在上的顺序堆在货架上;拿到 1 时可以直接装填;拿到 3 时又得临时码放在 6 号颜色箱上;拿到 2 时可以直接装填;随后从货架顶取下 3 进行装填;然后拿到 5,临时码放到 6 上面;最后取了 4 号颜色直接装填;剩下的工作就是顺序从货架上取下 5、6、7 依次装填。

但如果工厂按照 3、1、5、4、2、6、7 这个顺序发货,工人就必须要愤怒地折腾货架了,因为装填完 2 号颜色以后,不把货架上的多个箱子搬下来到 3 号箱,就不可能顺利完成任务。

另外,货架的容量有限,如果要堆积的货物超过容量,工人也没办法顺利完成任务。例如工厂按照 7、6、5、4、3、2、1 这个顺序发货,如果货架够高,能码放 6 只箱子,那还是可以顺利完工的;但如果货架只能码放 5 只箱子,工人就又要愤怒了……

本题就请你判断一下,工厂的发货顺序能否让工人顺利完成任务。

输入格式:

输入首先在第一行给出 3 个正整数,分别是彩虹瓶的颜色数量 N(1<N≤10​3​​)、临时货架的容量 M(<N)、以及需要判断的发货顺序的数量 K。

随后 K 行,每行给出 N 个数字,是 1 到N 的一个排列,对应工厂的发货顺序。

一行中的数字都以空格分隔。

输出格式:

对每个发货顺序,如果工人可以愉快完工,就在一行中输出 YES;否则输出 NO

输入样例:

7 5 3
7 6 1 3 2 5 4
3 1 5 4 2 6 7
7 6 5 4 3 2 1

输出样例:

YES
NO
NO

思路:

这题玩文字游戏。。理解错题意了,想得过于复杂。重点语句已在前面的题目里标红。

我错误的理解为如果当前需要装入物品如果压在临时货物底下的话可以先将上面的物品搬下取出装入再将搬下的临时货物

码放回去。。其实不用模拟的那么复杂,只要判断出工人是愤怒还是愉快的完成工作就可以了。

代码:

第一个点不对。。

import java.util.Scanner;
import java.util.Stack;
public class 彩虹瓶 {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
        Scanner in=new Scanner(System.in);
        int N=in.nextInt();
        int M=in.nextInt();
        int K=in.nextInt();
        int count=0;
        while(count<K){
        	Stack<Integer> st=new Stack<Integer>();
        	int num=1;//当前需要的货物号
        	int tmp;//当前搬来的货物号
        	for(int i=0;i<N;i++){
        		if(st.size()>M){//临时货架容量不够,愤怒
        			break;
        		}
        		tmp=in.nextInt();
        		if(tmp==num){
        			num++;
        			while(!st.isEmpty()&&st.peek()==num){
        				num++;
        				st.pop();
        			}
        		}
        		else{
        			st.push(tmp);
        		}
        		if(i==N-1&&!st.isEmpty()&&st.peek()!=num){//得搬下来多个箱子,愤怒
        			break;
        		}
        	}
        	if(st.isEmpty()){
        		System.out.println("YES");
        	}
        	else{
        		System.out.println("NO");
        	}
        	count++;
        }
        
	}

}

青蛙跳台阶 (10分)

一只青蛙一次可以跳上 1 级台阶,也可以跳上2 级。求该青蛙跳上一个n 级的台阶总共有多少种跳法。

输入格式:

首先输入数字n,代表接下来有n组输入,50>=n>=0,然后每行一个数字,代表台阶数,数字为小于60的整数

输出格式:

对每一组输入,输出青蛙的跳法。

输入样例:

3
1
2
3

输出样例:

1
2
3

思路:

子集树回溯剪枝,最后一个点超时。

在网上看了看正解,斐波那契数列,别用递归方式(最后一个点仍超时),用迭代方式。

如果n=1,只有一种跳法,那就是1
如果n=2,那么有两种跳法,2,[1,1]
如果n=3,那么有三种跳法,[1,1,1],  [1,2],  [2,1]
如果n=4,那么有五种跳法,[1,1,1,1] , [1,1,2] , [1,2,1] , [2,1,1],  [2,2]
如果n=5,那么有八种跳法,[1,1,1,1,1],  [1,1,1,2],  [1,1,2,1] , [1,2,1,1],  [2,1,1,1],  [2,2,1],  [2,1,2],  [1,2,2]
结果为1,2,3,5,8  即斐波那切数列

import java.util.Scanner;
public class 青蛙跳台阶 {
	static int count=0;
	static void dfs(int n,int sum){
		//n阶台阶,sum存储已走步数,count存储跳法数量
		if(sum==n){
			count++;
		}
		else{
			sum+=1;
			if(sum<=n){
				dfs(n,sum);
			}
			sum-=1;
			sum+=2;
			if(sum<=n){
				dfs(n,sum);
			}
			sum-=2;
		}
	}
	//也是斐波那契数列变形,递归最后一个点超时
	static int jump(int n){
		if(n==0){
			return 0;
		}
		if(n==1){
			return 1;
		}
		if(n==2){
			return 2;
		}
		return jump(n-1)+jump(n-2);
	}
	//正解
	public static long JumpFloor(long n)
    {
        long former1 = 1;
        long former2 = 2;
        long target = 0;
        if (n==0)
        {
            return 0;
        }
        if (n==1)
        {
            return 1;
        }
        if (n==2)
        {
            return 2;
        }
        else
        {
            for (int i = 3; i <= n; i++)
            {
                target = former1 + former2;
                former1 = former2;
                former2 = target;
            }
            return target;
        }
    }

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
        Scanner in=new Scanner(System.in);
        int n=in.nextInt();
        int[] a=new int[n];
        for(int i=0;i<n;i++){
        	a[i]=in.nextInt();
        }
        for(int i=0;i<n;i++){
//        	count=0;
//        	dfs(a[i],0);
//        	System.out.println(count);
        	System.out.println(JumpFloor(a[i]));
        }
	}

}

其实所谓的斐波那契数列只不过是动态规划而已!请看下面的DP解法。

代码:

一维数组DP

import java.util.Scanner;
public class 青蛙跳台阶DP {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
        Scanner in=new Scanner(System.in);
        int n=in.nextInt();
        int[] a=new int[n];
        for(int i=0;i<n;i++){
        	a[i]=in.nextInt();
        }
        int[] dp=new int[n+1];//dp[i]表示跳上n阶台阶有多少种跳法
        dp[0]=1;//切记注意此题dp[0]需为1,我感觉可以理解为C(2 0)=1
        dp[1]=1;
        for(int i =0;i<n;i++){
        	for(int j=2;j<=a[i];j++){
        		//第n阶台阶可由第n-1阶台阶或n-2阶台阶跳上
        		dp[j]=dp[j-1]+dp[j-2];
        	}
        	System.out.println(dp[a[i]]);
        }
	}

}

学习动态规划可refer to this literature

发布了81 篇原创文章 · 获赞 91 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44593822/article/details/103988458