运行要求
运行时间限制: 2sec
内存限制: 1024MB
原文链接
题目
你有N根竹竿子,这N根竹竿子的长度依次为l1,l2,l3..lN(单位是厘米)
你的任务是,从N根竹竿子里面选择一些竹竿子(全部选择也可以),用这些选出来的竹竿子,制作长度为A,B,C的3根竹竿子。为了完成这项任务,赋予你3种魔法。你可以任意使用这三种魔法,使用次数任意。
- 延长魔法:消耗1MP(魔法值),选择一根竹竿子,使它的长度增加1
- 缩短魔法:消耗1MP(魔法值),选择一根长度为2以上的竹竿子,使它的长度减少1
- 合成魔法:消耗10MP(魔法值),选择两根竹竿子,把它合成一根竹竿子。新的竹竿子是两根竹竿子的长度的和。(使用合成魔法合成的竹竿子同样可以使用其他魔法)
为了完成你的任务,你至少需要消耗多少MP?
输入前提条件
- 3<=N<=8
- 1<=C<B<A<=1000
- 1<=li<=1000
- 输入的所有的值为整数
输入
输入都以以下标准从命令行输入
N A B C
l1
l2
l3
.
.
.
lN
输出
输出需要的最少的魔法值
例1
输入
5 100 90 80
98
40
30
21
80
输出
23
从5根长度分别是98,40,30,21,80的竹竿子里制作长度为100,90,80的3根竹竿子
我们已经有长度为80的竹竿子,长度为90,100的竹竿子用下面的魔法得到
- 对长度为98的竹竿子,使用2次延长魔法,使其长度达到100(消耗MP:2)
- 对长度为30和40的竹竿子使用合成魔法,使其长度达到70(消耗MP:10)
- 对长度为21的竹竿子使用缩短魔法,使其长度达到20
- 使用合成魔法合唱2得到的70和3得到的20,合成长度为90的竹竿子
例2
输入
8 100 90 80
100
100
90
90
90
80
80
80
输出
0
已经准备好的竹竿子里面已经含有任务需要的竹竿子的话,我们不必使用魔法。
例3
输入
8 1000 800 100
300
333
400
444
500
555
600
666
输出
243
读懂题目
用一个数列ARR,里面有N个数。用这N个数,进行+1,-1,两两相加的方法,拼凑A,B,C。求最少的代价的方法。
+1,-1的代价是1
两两相加的方法的代价是10
解题思路
这一题咋一看,怎么办啊。合成魔法也就是拼接,貌似可以抽象成排列组合的问题。但是延长魔法(+1),缩短魔法(-1)貌似不好抽象成数学问题。
1根竹竿子,只能用在A,B,C的其中一个上。如果一根竹竿子想用在A上,又用在B上是不可能的,因为没有分解魔法…………^^
这样想的话,每一根竹竿子的归属有属于A,属于B,属于C,3种归属,并且每一根竹竿子只有一个归属
等一下,还有一种情况就是竹竿子没有被使用。因为题目条件没有说非得全部使用,也就是存在没有被使用的竹竿子的可能性。
因此每一根竹竿子的归属有属于A,属于B,属于C,没有被使用,4种归属
那么N个竹竿子,所有的情况考虑进来的话,应该有4的N次方种情况。我们看一看条件,这里N最大是8。所以最多的情况是4的8次方,65536种情况。O(N)的复杂度的话,时间上是来得及。
如何遍历4的N次方的所有情况,这里我们用到改进版的比特运算
那么每一种情况的MP如何求到呢。比如N=6,有6根竹竿子。
第1,2,3根竹竿子用来制作A,
4,5用来制作B,
6用来制作C
制作A所需要的MP是2次合成,然后A的长度-(1,2,3)的长度的和的绝对值
制作B所需要的MP是1次合成,然后A的长度-(4,5)的长度的和的绝对值
制作C所需要的MP是0次合成,然后A的长度-(6)的长度的和的绝对值
这一点细想就能知道
1,2,3都用开制作A的情况下,延长魔法对1使用或是对2使用,可以看作最终对合成后的竹竿子使用。
所以我们先使用合成魔法拼接,然后再考虑用延长或者缩短魔法。
A,B,C必须要有原材料,如果出现A,B,C没有原材料的情况,该情况不允许考虑
代码
比特运算解法
import math
N,A,B,C = map(int,input().split())
ARR = []
for i in range(N):
ARR.append(int(input()))
def calculate(n, arr, a, b, c):
result = []
for i in range(4 ** n):
materialA = set()
materialB = set()
materialC = set()
materialNone = set()
for j in range(n):
s = (i >> 2 * j & 1)
t = (i >> (2 * j + 1) & 1)
if (s == 0) and (t == 0):
materialA.add(j)
if (s == 1) and (t == 0):
materialB.add(j)
if (s == 0) and (t == 1):
materialC.add(j)
if (s == 1) and (t == 1):
materialNone.add(j)
ok, mp = judge(n, arr, a, b, c, materialA, materialB, materialC, materialNone)
if ok:
result.append(mp)
print(int(min(result)))
def judge(n, arr, a, b, c, materialA, materialB, materialC, materialNone):
if materialNone == n:
return False, -1
if len(materialA) == 0:
return False, -1
if len(materialB) == 0:
return False, -1
if len(materialC) == 0:
return False, -1
mpA = 0
a = a - calculateResult(arr,materialA)
mpA += math.fabs(a)
if len(materialA) > 1:
mpA += 10 * (len(materialA) - 1)
mpB = 0
b = b - calculateResult(arr,materialB)
mpB += math.fabs(b)
if len(materialB) > 1:
mpB += 10 * (len(materialB) - 1)
mpC = 0
c = c - calculateResult(arr,materialC)
mpC += math.fabs(c)
if len(materialC) > 1:
mpC += 10 * (len(materialC) - 1)
return True,mpA+mpB+mpC
def calculateResult(arr,material):
result = 0
for m in material:
result = result + arr[m]
return result
calculate(N, ARR, A, B, C)
dfs解法
import math
N, A, B, C = map(int, input().split())
ARR = []
for i in range(N):
ARR.append(int(input()))
result = []
def dfs(deep, crr):
if deep == N:
ok, res = calculate(crr)
if ok:
result.append(int(res))
else:
for i in range(4):
crr[deep] = i
dfs(deep + 1, crr)
def calculate(crr):
sA = []
sB = []
sC = []
sN = []
for index, cr in enumerate(crr):
if cr == 0:
sN.append(ARR[index])
if cr == 1:
sA.append(ARR[index])
if cr == 2:
sB.append(ARR[index])
if cr == 3:
sC.append(ARR[index])
if len(sA) == 0:
return False, -1
if len(sB) == 0:
return False, -1
if len(sC) == 0:
return False, -1
s1 = (len(sA) - 1) * 10 + math.fabs(sum(sA) - A)
s2 = (len(sB) - 1) * 10 + math.fabs(sum(sB) - B)
s3 = (len(sC) - 1) * 10 + math.fabs(sum(sC) - C)
return True, s1 + s2 + s3
dfs(0, [0 for i in range(N)])
print(min(result))
总结
这里考察了如何把魔法问题抽象成排列组合的数学问题的思维
另外对于如何遍历4的N次方的情况的方法也进行了考察
遍历的方法这里用到了比特运算。还有DFS的方法。
※ 另外,我会在我的微信个人订阅号上推出一些文章,欢迎关注
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。