糖果 蓝桥杯 重复覆盖问题 搜索

糖果店的老板一共有 M 种口味的糖果出售。

为了方便描述,我们将 M 种口味编号 1∼M。

小明希望能品尝到所有口味的糖果。

遗憾的是老板并不单独出售糖果,而是 K 颗一包整包出售。

幸好糖果包装上注明了其中 K 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。

给定 N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。

输入格式
第一行包含三个整数 N,M,K。

接下来 N 行每行 K 这整数 T1,T2,⋅⋅⋅,TK,代表一包糖果的口味。

输出格式
一个整数表示答案。

如果小明无法品尝所有口味,输出 −1。

数据范围
1≤N≤100,
1≤M,K≤20,
1≤Ti≤M
输入样例:
6 5 3
1 1 2
1 2 3
1 1 3
2 3 5
5 4 2
5 1 2
输出样例:
2

搜索的3个优化点

1.迭代加深(最重要的优化点)
每次以确定的迭代深度去尝试,不行再换更高的深度,但是深度也有界限,比如这个题目中深度最多为m,否则肯定无解了。

2.可行性剪枝

3.每次迭代找可选择性最少的列

import java.util.*;

public class Main
{
	static int n,m,k;
	static int N=105,M=1<<20;
	static int log2[]=new int[M];
	static Vector col[]=new Vector [N];
	
	static int lowbit(int x)
	{
		return x&-x;
	}
	// 最少需要再选几行  可行性剪枝
	static int h(int state)
	{
		int res=0;
		for(int i=(1<<m)-1-state;i!=0;i-=lowbit(i))
		{
			int c=log2[lowbit(i)];
			res++;
			for(int j=0;j<col[c].size();++j)
			{
				int row=(int)col[c].get(j);
				i&=~row;
			}
			
		}
		return res;
	}
	static boolean dfs(int depth, int state)
	{
		if(depth==0||h(state)>depth)return state==(1<<m)-1;
		
		// 找到选择性最少的一列
		int t=-1;
		for(int i=(1<<m)-1-state;i!=0;i-=lowbit(i))
		{
			int c=log2[lowbit(i)];
			if(t==-1||col[c].size()<col[t].size())t=c;
		}
		
		// 枚举选哪行
		for(int i=0;i<col[t].size();++i)
		{
			int row=(int) col[t].elementAt(i);
			if(dfs(depth-1,state|row)==true)
				return true;
		}
		return false;
	}
	
	public static void main(String args[])
	{
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		m=sc.nextInt();
		k=sc.nextInt();
		
		for(int i=0;i<m;++i)log2[1<<i]=i;
		for(int i=0;i<m;++i)col[i]= new Vector<>();
		
		for(int i=0;i<n;++i)
		{
			int state=0;
			for(int j=0;j<k;++j)
			{
				int c;
				c=sc.nextInt();
				state|=1<<(c-1);
			}
			for(int j=0;j<m;++j)
			{
				if(((state>>j)&1)==1)
				{
					col[j].addElement(state);
				}
			}
		}
		
		//迭代加深
		int depth=0;
		while(depth<=m&&dfs(depth,0)==false)depth++;
		
		if(depth>m)depth=-1;
		
		System.out.println(depth);
	}
}
posted @ 2022-11-17 23:03  林动  阅读(19)  评论(0编辑  收藏  举报