蒜厂幼儿园有n个小朋友,每个小朋友都有自己想玩的玩具。身为幼儿园园长的你决定给幼儿园买一批玩具,由于经费有限,你只能买m个玩具。已知玩具商店一共卖k种玩具,编号为1,2,3...k,你让每个小朋友想玩的玩具编号都写在了纸上,你希望满足尽可能多的小朋友的需求,请计算出最多同时满足多少个小朋友的玩具需求。
输入格式
第一行,输入三个整数n,m,k(1≤n≤100,1≤m≤k≤15)中间用空格分开。
接下来n行,第i+1(0≤i<n)行的第一个数字ai代表第i个小朋友想玩的玩具数量,接下来有ai个数字,代表这ai个玩具的标号。
输出格式
输出一个整数,表示最多能满足多少小朋友的玩具需求
样例输入
5 3 5 2 1 4 0 2 3 1 3 2 3 4 2 4 5
样例输出
3
解题思路
为了找到能满足最多的小朋友的需求,我们需要对所有的需求进行遍历并找到满足最多的那一种
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
public class Main {
static int n;
static List<Integer>[] data;
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
//再次输入n行
n=sc.nextInt();
//只能买m种玩具
int m=sc.nextInt();
//商店一共卖k种玩具
int k=sc.nextInt();
sc.nextLine();
data=new List[n];
//存储需求数据
for(int i=0;i<n;i++)
{
List<Integer> list=new ArrayList<Integer>();
int num=sc.nextInt();
for(int j=0;j<num;j++)
{
list.add(sc.nextInt());
}
data[i]=list;
}
int a=zuhe(0,m,new HashMap<Integer,Integer>(),0);
System.out.print(a);
}
public static List<String> zuHeList=new ArrayList<String>();
public static int pointer=-1;
//顺序 剩余额度 方案 满足人数
public static int zuhe(int index,int leave,Map<Integer,Integer> map,int people){
System.out.println(index);
//已经检查过第k个人的需求或无法尝试新内容
Map<Integer,Integer> map3=cloneMap(map);
//定位到出错的地方,当leave=0时,不一定就停止,因为可能不需要购买多余的玩具就能满足要求
//分成三种情况来处理 已经完成要求的遍历 或没有在向下遍历的可能
if(index==n||leave<0)
{
return people;
}
//没有要求的人直接满足
else if(data[index]==null||data[index].size()==0){
return zuhe(index+1,leave,map,people+1);
}
//尝试满足要求或者不满足要求当前的小朋友
else
{
List insideList=data[index];
int a=zuhe(index+1,leave,map,people);
int b=0;
//需要额外购买的商品的计数器
int thingsToBuyCounter=0;
for(int i=0;i<insideList.size();i++)
{
if(!map3.containsKey(insideList.get(i)))
{
thingsToBuyCounter++;
map3.put((Integer)insideList.get(i), 1);
}
}
if(leave>=thingsToBuyCounter)
b=zuhe(index+1,leave-thingsToBuyCounter,map3,people+1);
return Math.max(a,b);
}
}
//深度复制map元素
public static Map<Integer,Integer> cloneMap(Map<Integer,Integer> map)
{
Map newMap=new HashMap<Integer,Integer>();
Iterator iterator=map.keySet().iterator();
while(iterator.hasNext())
{
newMap.put(iterator.next(), 1);
}
return newMap;
}
}
首先是尝试动态规划的方法,我们只需要知道那种情况的满足的数量最大,因此设置了函数in zuhe(),通过递归比较添加新物品满足小朋友或跳过当前小朋友两种情况,并最终得到最大值
但是此方法处理后两组测试用例超时,当测试用例规模很大时,递归层数会非常的深
第二种思路是采用二进制枚举的方式,声明一个长度为k的二进制数据标记当前选定的情况,假如一共有4种商品,0001表示选择第四种商品,1001表示同时选择第一第四种商品以此类推。
我们从0开始枚举,到1<<k (2^k-1),这就是所有商品选择可能的情况
接下来,将10进制数据转化为二进制的形式,存储到数组中
检查选取的商品数是否符合要求,不符合直接测试下一个情况
再逐个人进行匹配,统计符合的人数
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static int n;
static int m;
static int k;
public static void main(String[] args) {
// TODO Auto-generated method stub
//匹配所有的不大于长度的情况进行枚举,计数符合条件的情况
Scanner sc=new Scanner(System.in);
//n个小朋友
n=sc.nextInt();
//只能买m个玩具
m=sc.nextInt();
//商店一共卖k种
k=sc.nextInt();
//array数组的存储方式是相反的,即第1个礼物存储到数组中第一个
int[][] array=new int[n][16];
int[] sum=new int[1<<k];
//输入数据并存储
for(int i=0;i<n;i++){
int num=sc.nextInt();
array[i][0]=num;
for(int j=0;j<num;j++){
//存储二进制格式
array[i][sc.nextInt()]=1;
}
for(int j=1;j<num;j++)
{
if(array[i][j]!=1)
array[i][j]=0;
}
//sc.nextLine();
}
//开始枚举
for(int i=0;i<(1<<k);i++)
{
//与m位长相匹配,测试数据的01计数器
int testtot_0=0;
int testtot_1=0;
int mathValue[]=new int[16];
int start=1;
//还原回二进制数据
for(int k=i;k>0;k/=2){
mathValue[start]=k%2;
start++;
if(k%2==1){
testtot_1++;
}else{
testtot_0++;
}
}
//枚举数字的1的个数不匹配
if(testtot_1!=m)
{
continue;
}
//开始搜索需求,从第0个人开始枚举
for(int j=0;j<array.length;j++)
{
int tot_0=0;
int tot_1=0;
//统计需求长度,淘汰需求长度超标的需求
for(int k=0;k<array[j].length;k++){
if(array[j][k]==1){
tot_1++;
}else{
tot_0++;
}
}//淘汰
if(tot_1>m)
{
continue;
}
else{
//标记是否匹配成功
boolean state=true;
int test=1;
for(int z=1;z<16;z++)
{
//这种情况就是当生成的测试数据为0但是要求的数据缺是1,中途退出就是匹配终端
if(mathValue[z]<array[j][z])
{
state=false;
break;
}
}
if(state)
{
sum[i]=sum[i]+1;
}
}
}
}
//结果存储到sum中,并排序
Arrays.sort(sum);
System.out.print(sum[sum.length-1]);
}
}