网易互娱2018秋招笔试——求从N道题中选M道符合条件的题的总情况

题目描述

马上又到一年一度的网易校招了,HR找到小Y,告诉他要开始准备今年的机考题目了。小Y和小伙伴们日以继夜、废寝忘食地出好了N道题目,并且给每道题目打了难度1~5, 同时 给每道题目标记了相应的tag(一道题目可能有多个tag),表示这道题目所考察的知识点。

由于机考时间有限,小Y需要从N到候选题目中挑选出M道题目组成最终的考卷,为了挑选最合适的同学,并且使得题目具备一定的区分度,同时考察的知识面尽量广,这M道题目还需要满足下列条件:

(1)所有M道题目的总难度不超过指定的值L。

(2)所有M道题目的考察点都没有重复,即所有的tag都没有重复。

(3)为了具备区分度,难度最低的题目相比难度最高的题目的难度差不低于S。

小Y想知道满足上述条件的总方案数有多少种,你能告诉他吗?

输入描述:

每个输入数据包含多个测试点。

第一行为测试点的个数T(T<=10)。

每个测试点第1行包含4个数字:N(1<N<=16),M(1<M<=N),L(0<=L<=100),S(0<=S<=4)。

然后是N行,每行代表一道题的信息。每行首先是题目的难度X(1<=X<=5),然后一个整数P(0<P<=5)表示这道题目的tag数目,后面接着P个以空格间隔的字符串表示tag,每个字符串均由小写字母组成,长度不超过10,保证每道题不会包含一样的tag。

输出描述:

对于每个测试点,输出一行,表示总的方案数。

示例

输入

2
5 2 5 1
1 2 string dfs
1 1 heap
2 1 dfs
3 2 math search
4 1 dp
5 2 5 0
1 2 string dfs
1 1 heap
2 1 dfs
3 2 math search
4 1 dp

输出

6
7

解题思路

该题的主要难点在于如何遍历从N道题中选择M道题的所有情况,对于每种情况再进行条件满足判断。

采用动态规划的思路进行遍历所有情况,再判断是否满足总难度、最大难度差和tag重复问题,tag重复问题采用Set集合类进行判断。

主如下:

import java.util.ArrayList;
import java.util.Scanner;
import java.util.TreeSet;

public class DynamicProgram {
	// 结果集
	static int[] result;
	// 需求集
	static String[][] demands;

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		int all = Integer.parseInt(scanner.nextLine().trim());
		// 成员初始化
		result = new int[all];
		demands = new String[all][];
		// 存所有测试点的所有题目信息
		ArrayList<String[][]> conditions = new ArrayList<>();
		for (int i = 0; i < all; i++) {
			// 存第i个测试点的需求
			demands[i] = scanner.nextLine().trim().split(" ");
			// 存第i个测试点的所有题目信息
			String[][] condition = new String[Integer.parseInt(demands[i][0])][];
			for (int j = 0; j < Integer.parseInt(demands[i][0]); j++) {
				condition[j] = scanner.nextLine().trim().split(" ");
			}
			conditions.add(condition);
		}
		// 遍历所有测试点,进行条件判断
		for (int i = 0; i < all; i++) {
			int[] hasCondition = new int[Integer.parseInt(demands[i][1])];
			combine(conditions.get(i), conditions.get(i).length, Integer.parseInt(demands[i][1]), hasCondition, i);
		}
		for (int out : result)
			System.out.println(out);
	}

	/**
	 * 
	 * @param condition 该测试点下的所有条件
	 * @param num 该测试点下的条件总数
	 * @param m 第i种情况下选择的条件,长度为M的个数
	 * @param hasCondition 第i种情况下选择的条件,长度为M的个数
	 * @param demandIndex 第demandIndex个测试点,用于在满足条件时,第demandIndex个测试点的结果加一
	 */
	public static void combine(String[][] condition, int num, int m, int[] hasCondition, int demandIndex) {
		for (int i = num; i >= m; i--) {
			hasCondition[m - 1] = i - 1;
			if (m > 1) {// m>1,表示还未选择m个条件,继续迭代
				combine(condition, i - 1, m - 1, hasCondition, demandIndex);
			} else {// 满足条件m个,进行条件满足判断
				checkCondition(condition, hasCondition, demandIndex);
			}
		}
	}

	/**
	 * 判断第i种情况下是否满足条件,满足则result加一
	 * @param condition 该测试点下的所有条件
	 * @param hasCondition 第i种情况下选择的条件,长度为M的个数
	 * @param demandIndex 第demandIndex个测试点,用于在满足条件时,第demandIndex个测试点的结果加一
	 */
	public static void checkCondition(String[][] condition, int[] hasCondition, int demandIndex) {
		int l = Integer.parseInt(demands[demandIndex][2]);
		int s = Integer.parseInt(demands[demandIndex][3]);
		TreeSet<String> tags = new TreeSet<>();
		// 该情况下的总难度
		int sum = 0;
		// 最小难度值
		int minDifficulty = Integer.parseInt(condition[hasCondition[0]][0]);
		// 最大难度值
		int maxDifficulty = Integer.parseInt(condition[hasCondition[0]][0]);
		for (int i = 0; i < hasCondition.length; i++) {// 遍历该情况下的所有条件
			String[] cond = condition[hasCondition[i]];
			int difficulty = Integer.parseInt(cond[0]);
			// 比较难度值是否最小
			minDifficulty = Math.min(minDifficulty, difficulty);
			// 比较难度值是否最大
			maxDifficulty = Math.max(maxDifficulty, difficulty);
			sum += Integer.parseInt(cond[0]);
			// 采用Set集合类判断tag是否有重复
			int tagNum = Integer.parseInt(cond[1]);
			for (int j = 0; j < tagNum; j++) {
				if (tags.contains(cond[j + 2])) {
					return;
				} else {
					tags.add(cond[j + 2]);
				}
			}
		}
		// 判断总难度是否合格
		if (sum > l)
			return;
		// 判断最大难度差是否合格
		if (maxDifficulty - minDifficulty < s) {
			return;
		}
		// 满足条件,第demandIndex个测试点的结果加一
		result[demandIndex]++;
	}
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值