题目描述
样例
思路
对于糖果的种类k,每种糖果存在选或者不被选的情况,因此可以将其理解为二进制数例如,当k为5的时候,00001就可以表示1,2,3,4号糖果没被选,5号糖果被选,再如,10100就表示1号和3号糖果被选择了,那么如何实现这种二进制表示状态呢?这就是今天的主角:状态压缩,即利用二进制来表示集合中元素的各种状态,首先我们需要了解状态压缩涉及到的位运算:
·~:按位取反运算符,如~10=01
·&:按位与运算符,其口诀是:同一为一,其余为零,例如:10011&01001=00001
·|:按位或运算符,其口诀是:同零为零,其余为一
·<<:左移一位操作,相当于将值乘以2,例如1<<2就等于100,即1*2*2
·>>:同上
详细的位运算以及状压dp基础入门请参考这位大佬的博客:
https://www.cnblogs.com/ljy-endl/p/11627018.html
当我们理解到了状压dp的思路之后,我们再来详细的捋一捋这道题,在n个里面选择一定数量的糖果包,使得达到最终状态花费最少,这实际上就是我们的背包模型,因此我们可以采用背包的方法,将所有状态遍历一次,然后用每一包糖果对可能情形进行松弛一遍,最后输出即可
AC代码
import java.util.Arrays;
import java.util.Scanner;
public class 糖果 {
static int n,m,k,dp[]=new int[1<<20],arr[];//dp数组存储状态,这里假设有20种糖果,arr记录每包糖果的情况
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
n=scanner.nextInt();
m=scanner.nextInt();
k=scanner.nextInt();
Arrays.fill(dp,Integer.MAX_VALUE);//初始化dp数组
arr=new int[n];
for (int i = 0; i < n; i++) {
int sta=0;
for (int j = 0; j < k; j++) {
int s=scanner.nextInt();
sta=sta|(1<<(s-1));//得到该种糖果的状态
}
arr[i]=sta;//存储该状态
dp[sta]=1;//表示到达该状态至少要花费1包糖果
}
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < 1<<m; j++) {//遍历所有状态
if(dp[j]==Integer.MAX_VALUE) continue;//无法达到该种情况则continue
dp[j|arr[i]]=Math.min(dp[j]+1,dp[j|arr[i]]);//核心代码,作用是松弛结果
}
}
if(dp[(1<<m)-1]==Integer.MAX_VALUE) System.out.println(-1);
else System.out.println(dp[(1<<m)-1]);//(1<<m)-1的结果是全1,即全部糖果种类都含有
}
}