各类算法笔试题汇总

1. 假设某卡牌类游戏里面,你搜集了 17 种卡牌,每种卡牌的攻击伤害率从小到大排列分别 为 1,4,9,… ,289,即 n 的平方(1<=n<=17,n 为正整数),并且每种卡牌的数量足够 多,现在请你计算出有多少种卡牌组合方式使得卡牌的总攻击率为 m(m<=500)比如 m=10, 那么有 4 种组合卡牌的方式,相同种卡牌可重复出现; 


【分析】 此类问题属于组合计数问题,在组合数学中,最经典的问题就是给定n个不同类型的砝码(单个类型的砝码数不固定),求当给定一个值时,求解能够完成称重的所有组合数。而此类问题一般可通过“生成函数(母函数)”的方法来解决。但这种方法再实际编程过程中,往往需要求解出一个通向式。故在此,我们将选取另一种方法来解决该问题。


首先,定义函数:   findNumber(E,n,energy)  , 其中 n = Math.sqrt(E) ; 该函数表示当总攻击率为E时,采用前n种卡牌进行组合,最终能达到目标的组合数。而对于上述问题,可以分解为两个子问题:①使用前n-1种选择组成E的组合数  ②至少使用第n种选择一次时组成m的组合数。 这样问题就变得迎刃而解了。采用递归的方式,将上述子问题相加即可得到最终的结果。


以下为该问题的JAVA代码:

import java.util.Scanner;
public class Main {

	public int  findNumber(int E, int n, int[] energy) {
		
		if(E == 0)
			return 1;
		if( E < 0 || n <= 0) 
			return 0;
		
		return findNumber(E, n-1, energy) + findNumber(E - energy[n], n, energy);
	}
	
	
	public static void main(String[] args) {
		
		int[] energy = new int[18];
		for(int i=0; i


2.题目要求:

输入: 第一行三个数n,m,k,分别表示n个数,取m个,且m个中的任意两个位置差要大于等于K,接下来一行,有n个整数,表示序列上的每个数。

输出: 最大和

 

数据范围:  n<=10000,  m<=100,m<=n


Sample Input

 4 2 2

 3 4 -5 1 

Sample Output

 5


【分析】首先,请注意题目中标红的文字! 对于本体,很容易想到解题方向:动态规划;也很容易找到动态规划中的状态转移方程。在这里,我们首先定义F[n][m],表示从前n个数中,取m个数,并且满足位置差条件的数之和。那么对于动态规划问题,第一步先确定“状态转移方程”,在给定条件下(位置差),状态转移方程可记为:

F[ i ] [ j ] = max ( F[ i - 1 ] [ j ] ,F[ i - k ] [ j - 1 ] )

即分两种情况讨论,①不取第i个数,在前i-1个数中找和最大的  ②取第i个数,在前i - k 个数中找和最大的 ; 最后比较①②两种情况,将大的一方作为当前情况的和最大。当状态转移方程确定后,需要确定初始状态的值,而这个值可手动给出,即:F[0][0], F[1][0],....,F[n-1][0]  和 F[0][1],F[0][2],.....,F[0][m-1] 。 


状态转移方程初始状态确定后,本题的问题也就迎刃而解了。


以下为该问题的JAVA代码

import java.util.Scanner;
public class Main {

	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		while(sc.hasNextLine()) {
			int n = sc.nextInt();
			int m = sc.nextInt();
			int k = sc.nextInt();
			
			int[] A = new int[n];
			int[][] F = new int[n][m];
			
			for(int i=0; i0 && F[i-1][0]0 && F[i-1][0]>=A[i])
					F[i][0] = F[i-1][0];
			}
			for (int j = 1; j < m; j++) {
				F[0][j] = 0;
			}
			
			int reslut = getMaxData(A, F, n, m, k);
			System.out.println(reslut);
		}
	}

	private static int getMaxData(int[] A, int[][] F, int n, int m, int k) {
		k--;
		for (int i = 1; i < n; i++) {
			for (int j = 1; j < m; j++) {
				F[i][j] = F[i-1][j];
				if(i >= k) {
					F[i][j] = F[i-1][j] > (F[i-k][j-1] + A[i]) ? F[i-1][j] : (F[i-k][j-1] + A[i]);
				}
			}
		}
		return F[n-1][m-1];
	}
}

3.最长公共子序列

【分析】

 ①  序列str1和序列str2

  ·长度分别为m和n;

  ·创建1个二维数组L[m.n];
  ·初始化L数组内容为0
  ·m和n分别从0开始,m++,n++循环:
       - 如果str1[m] == str2[n],则L[m,n] = L[m - 1, n -1] + 1;
       - 如果str1[m] != str2[n],则L[m,n] = max{L[m,n - 1],L[m - 1, n]}
  ·最后从L[m,n]中的数字一定是最大的,且这个数字就是最长公共子序列的长度
  ·从数组L中找出一个最长的公共子序列

②  从数组L中查找一个最长的公共子序列
  ·i和j分别从m,n开始,递减循环直到i = 0,j = 0。其中,m和n分别为两个串的长度。
       - 如果str1[i] == str2[j],则将str[i]字符插入到子序列内,i--,j--;
       - 如果str1[i] != str[j],则比较L[i,j-1]与L[i-1,j],L[i,j-1]大,则j--,否则i--;(如果相等,则任选一个)

以下为本题目的JAVA代码
import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		while( sc.hasNext()) {
			
			String A = sc.nextLine();
			String B = sc.nextLine();
			
			char[] ca = A.toCharArray();
			char[] cb = B.toCharArray();
			
			int la = A.length();
			int lb = B.length();
			
			int[][] F = new int[la+1][lb+1];
			
			for(int i=0; i t2) 
							F[m][n] = t1;
						else 
							F[m][n] = t2;
					}
				}
			}
			
			int tm = la, tn = lb;
			StringBuffer sb = new StringBuffer();
			
			while(tm > 0 && tn > 0) {
				if(ca[tm-1] == cb[tn-1]) {
					sb.insert(0, ca[tm-1]);
					tm--;
					tn--;
				}
				else if (F[tm-1][tn] > F[tm][tn-1]) 
					tm--;
				else 
					tn--;
			}
			
			System.out.println(F[la][lb]);
			System.out.println(sb.toString());
		}
	}
}

4.回文字符串
【输入】
输入一个字符串,字符串长度不超过1000.
【输出】
输出所需添加的最少字符数,使字符串满足回文字符串的条件。

样例输入
Ab3bd
样例输出

2



【分析】
第一种方法
         由于前面刚做完最长公共子序列的问题,对应本题而言,只需求解当前字符串和当前字符串的逆序字符串的LCS,用原字符串长度减去LCS长度即为本体的最终答案。

以下为该种方法的JAVA代码
import java.util.Scanner;

public class Main {

	private static int[][] getLCS(String A, String B) {
		
		char[] ca = A.toCharArray();
		char[] cb = B.toCharArray();
		
		int la = A.length();
		int lb = B.length();
		
		int[][] F = new int[la+1][lb+1];
		
		for(int i=0; i t2) 
						F[m][n] = t1;
					else 
						F[m][n] = t2;
				}
			}
		}
		return F;
	}
	
	private static void printLCS(int[][] F, String A, String B) {
		char[] ca = A.toCharArray();
		char[] cb = B.toCharArray();
		
		int tm = A.length();
		int tn = B.length();
		
		StringBuffer sb = new StringBuffer();
		
		while(tm > 0 && tn > 0) {
			if(ca[tm-1] == cb[tn-1]) {
				sb.insert(0, ca[tm-1]);
				tm--;
				tn--;
			}
			else if (F[tm-1][tn] > F[tm][tn-1]) 
				tm--;
			else 
				tn--;
		}
		System.out.println(F[A.length()][B.length()]);
		System.out.println(sb.toString());
	}
	
	private static int toPalindromeString(String data) {
		StringBuffer sb = new StringBuffer(data);
		String reverse_data = sb.reverse().toString();
		int[][] F = getLCS(data,reverse_data);
		int original_length = data.length();
		return original_length - F[original_length][original_length];
	}
	
	public static void main(String[] args) {
		
		Scanner sc = new Scanner(System.in);
		while( sc.hasNext()) {
			String data = sc.nextLine();
			int number = toPalindromeString(data);
			System.out.println(number);
		}
	}
}

第二种方法
        递归的方式
       int XXX(char[]c, int m, int n)表示:对于数组c,求解开始字符为m,结束字符为n组成回环字符串时需要添加字母的个数。
       
       递归出口:
          ① m >= n返回0
           ②m+1 == n相邻两个字母组成回环字符的串的情况
                   --c[m]= c[n]返回0
                    --c[m]!= c[n]
                      分为两种情况:
                          I. A[m]添加匹配项,则等价于XXX(c,m+1,n) + 1
                           II. A[n]添加匹配项,则等价于XXX(c,m,n-1) + 1

                          比较III的结果,取较小的返回!


以下为该种方法的JAVA代码
import java.util.Scanner;

/*回文字符串*/

public class Main {

	private static int toPalindromeString_other2(char[] c, int m, int n) {
		if(m >= n)
			return 0;
		if(m+1 == n)
			return 1;
		if(c[m] == c[n])
			return 0;
		else {
			int left = toPalindromeString_other2(c,m,n-1)+1;
			int right = toPalindromeString_other2(c,m+1,n)+1;
			if(left > right)
				return right;
			else 
				return left;
		}
	}
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while( sc.hasNext()) {
			String data = sc.nextLine();
			int number = toPalindromeString_other2(data.toCharArray(),0,data.length()-1);
			System.out.println(number);
		}
	}
}


5.最长递增子序列长度


import java.util.Scanner;

public class main {
	private static int getLIS(String data) {
		if(data.length() == 0)
			return 0;
		char[] d = data.toCharArray();
		int len = d.length;
		int[] F = new int[len];
		F[0] = 1;
		for(int i=1; i d[i-1])
				F[i] = F[i-1] + 1;
			else if (d[i] == d[i-1]) 
				F[i] = F[i-1] ;
			else {
				int tmp = 0;
				int j = i-1;
				while(j>=0) {
					if(d[i] > d[j]) {
						tmp = F[j] + 1;
						break;
					}
					else if (d[i] == d[j]) {
						tmp = F[j];
						break;
					}
					j--;
				}
				if(tmp > F[i-1])
					F[i] = tmp;
				else 
					F[i] = F[i-1];
			}
		}
		return F[len - 1];
	}
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while( sc.hasNext()) {
			String data = sc.nextLine();
			System.out.println(getLIS(data));
		}
	}
}




你可能感兴趣的:(各类算法笔试题汇总)