运行要求
运行时间限制: 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的竹竿子用下面的魔法得到

  1. 对长度为98的竹竿子,使用2次延长魔法,使其长度达到100(消耗MP:2)
  2. 对长度为30和40的竹竿子使用合成魔法,使其长度达到70(消耗MP:10)
  3. 对长度为21的竹竿子使用缩短魔法,使其长度达到20
  4. 使用合成魔法合唱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

名称未設定.001.jpeg

解题思路
这一题咋一看,怎么办啊。合成魔法也就是拼接,貌似可以抽象成排列组合的问题。但是延长魔法(+1),缩短魔法(-1)貌似不好抽象成数学问题。

1根竹竿子,只能用在A,B,C的其中一个上。如果一根竹竿子想用在A上,又用在B上是不可能的,因为没有分解魔法…………^^

这样想的话,每一根竹竿子的归属有属于A,属于B,属于C,3种归属,并且每一根竹竿子只有一个归属
名称未設定.002.jpeg

等一下,还有一种情况就是竹竿子没有被使用。因为题目条件没有说非得全部使用,也就是存在没有被使用的竹竿子的可能性。
名称未設定.003.jpeg

因此每一根竹竿子的归属有属于A,属于B,属于C,没有被使用,4种归属

那么N个竹竿子,所有的情况考虑进来的话,应该有4的N次方种情况。我们看一看条件,这里N最大是8。所以最多的情况是4的8次方,65536种情况。O(N)的复杂度的话,时间上是来得及。

如何遍历4的N次方的所有情况,这里我们用到改进版的比特运算
名称未設定.005.jpeg

那么每一种情况的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使用,可以看作最终对合成后的竹竿子使用。
所以我们先使用合成魔法拼接,然后再考虑用延长或者缩短魔法。
名称未設定.006.jpeg

A,B,C必须要有原材料,如果出现A,B,C没有原材料的情况,该情况不允许考虑
名称未設定.006.jpeg

代码
比特运算解法

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的方法。

※ 另外,我会在我的微信个人订阅号上推出一些文章,欢迎关注
二维码.jpg



伟大不DIAO
1 声望1 粉丝

Done is better than perfect