蓝桥杯 2019 年省赛 A 组 I 题
题目描述
糖果店的老板一共有 M M M 种口味的糖果出售。为了方便描述,我们将 M M M 种口味编号 1 1 1 ∼ M M M。
小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而是 K K K 颗一包整包出售。
幸好糖果包装上注明了其中 K K K 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。
给定 N N N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。
输入格式
第一行包含三个整数 N N N、 M M M 和 K K K。
接下来 N N N 行每行 K K K 这整数 T 1 , T 2 , ⋯ , T K T_1,T_2, \cdots ,T_K T1,T2,⋯,TK,代表一包糖果的口味。
输出格式
一个整数表示答案。如果小明无法品尝所有口味,输出 − 1 -1 −1。
样例 #1
样例输入 #1
6 5 3
1 1 2
1 2 3
1 1 3
2 3 5
5 4 2
5 1 2
样例输出 #1
2
提示
对于 30 % 30\% 30% 的评测用例, 1 ≤ N ≤ 20 1 \le N \le 20 1≤N≤20。
对于所有评测样例, 1 ≤ N ≤ 100 1 \le N \le 100 1≤N≤100, 1 ≤ M ≤ 20 1 \le M \le 20 1≤M≤20, 1 ≤ K ≤ 20 1 \le K \le 20 1≤K≤20, 1 ≤ T i ≤ M 1 \le T_i \le M 1≤Ti≤M。
思路
1.DP下标及含义
DP[i]
表示口味组合为i的二进制(若i为6即00110)情况下最少需要的糖果包数
在做输入的时候要注意状态的表示只能用或运算符,而不能用加,否则会导致相同的糖类重复计算
2.状态转移方程
F
[
j
∣
a
[
i
]
]
=
m
i
n
(
F
[
j
∣
a
[
i
]
]
,
F
[
j
]
+
1
)
F[j|a[i]] = min(F[j|a[i]], F[j]+1)
F[j∣a[i]]=min(F[j∣a[i]],F[j]+1)
当前口味组合加上a[i]
包糖所含的口味组合,看是否能变小
如果F[j]+1
更小,说明当前组合下有更优解
3.初始化
因为N最大值为100包,设置初始化DP数组均为105,表示无法尝到所有味道
F[0]
为0,表示没有口味时,需要买0包糖
最终求的是
F
[
2
M
−
1
]
F[2^M-1]
F[2M−1],即在
2
M
−
1
2^M-1
2M−1状态下最小糖果包数
4.遍历顺序
i:
0
−
>
N
0->N
0−>N遍历所有糖果包
j:
0
−
>
2
M
−
1
0-> 2^M-1
0−>2M−1遍历所有组合
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int N = scan.nextInt();
int M = scan.nextInt();
int K = scan.nextInt();
int[] candys = new int[105];
int[] dp = new int[1<<20+1];
for(int i=0;i<N;i++) {
for(int j=0;j<K;j++) {
int t = scan.nextInt();
candys[i] = candys[i]|1<<(t-1);
}
}
for(int i=0;i<1<<20;i++) {
dp[i] = 105;
}
dp[0] = 0;
for(int i=0;i<N;i++) {
for(int j=0;j<1<<M;j++) {
int temp = Math.min(dp[j|candys[i]], dp[j]+1);
// System.out.println(temp);
dp[j|candys[i]] = temp;
}
}
int ans = dp[(1<<M)-1]==105? -1:dp[(1<<M)-1];
System.out.print(ans);
}
}