状压dp:二进制状态+位运算
状态是二进制表示,十进制数是这个状态的名字。在本题中,状态state是m位二进制数,m是口味编号,1代表吃了,0代表没吃到,101100表示吃了第三,四,六个口味的糖(因为二进制数是低位在右高位在左,编号从右往左)dp[state]表示到达这个状态买最少几包糖。初始化无穷大,因为更新dp的值是找小的更新,dp[0]=0表示一个口味都不吃需要买0包。
wd=wd|(1<<(candy-1)) 味道相与把这一包糖的味道用二进制表示,与的特点就是只有0|0=0其余都为一,也不用考虑重复了,比如口味112就是011,123就是111,candy-1是因为二进制最低位是2**0,而糖果口味编号从1开始。
状态转移方程:dp[state|wd]=min(dp[state|wd],dp[state]+1),state|wd就是在state的基础上吃了一包wd的糖,比如说万一一包糖的味道都在state里面,以前都吃过了,那么dp[state|wd]=dp[state]<dp[state]+1,保持值不变,万一有没吃过的,dp[state|wd]就是比较当前值和上一个状态买这包糖,哪个买的少,也就是说不同的路到这个状态找一个最小值。
1<<m表示1后面带m个0,表示2**m,而2**m-1才表示m个1,就是m个口味都吃过了。
n,m,k=map(int,input().split())#m种口味,n包糖,一包k个
dp=[float('inf') for _ in range(1<<m)]#2**m个数表示1到m个口味被尝过的状态
dp[0]=0
candies=[]
for _ in range(n):
candi=list(map(int,input().split()))
candies.append(candi)
for packet in candies:
wd=0#糖果口味1,转化为1(1),糖果口味为2,转化为10(2),3变成100(4),1左移candy-1位
for candy in packet:
wd=wd|(1<<(candy-1))
for state in range(1<<m):
dp[state|wd]=min(dp[state|wd],dp[state]+1)
all_flavors = (1 << m) - 1
if dp[all_flavors] != float('inf'):
print(dp[all_flavors])
else:
print('-1')