AtCoder Context ABC 119- C - Synthetic Kadomatsu(比特運算解法)

運行要求
運行時間限制: 2sec
內存限制: 1024MB
原文連接微信

題目
你有N根竹竿子,這N根竹竿子的長度依次爲l1,l2,l3..lN(單位是釐米)
你的任務是,從N根竹竿子裏面選擇一些竹竿子(所有選擇也能夠),用這些選出來的竹竿子,製做長度爲A,B,C的3根竹竿子。爲了完成這項任務,賦予你3種魔法。你能夠任意使用這三種魔法,使用次數任意。app

  • 延長魔法:消耗1MP(魔法值),選擇一根竹竿子,使它的長度增長1
  • 縮短魔法:消耗1MP(魔法值),選擇一根長度爲2以上的竹竿子,使它的長度減小1
  • 合成魔法:消耗10MP(魔法值),選擇兩根竹竿子,把它合成一根竹竿子。新的竹竿子是兩根竹竿子的長度的和。(使用合成魔法合成的竹竿子一樣可使用其餘魔法)

爲了完成你的任務,你至少須要消耗多少MP?spa

輸入前提條件命令行

  • 3<=N<=8
  • 1<=C<B<A<=1000
  • 1<=li<=1000
  • 輸入的全部的值爲整數

輸入
輸入都以如下標準從命令行輸入3d

N A B C
l1
l2
l3
.
.
.
lN

輸出code

輸出須要的最少的魔法值

例1
輸入blog

5 100 90 80
98
40
30
21
80

輸出內存

23

從5根長度分別是98,40,30,21,80的竹竿子裏製做長度爲100,90,80的3根竹竿子
咱們已經有長度爲80的竹竿子,長度爲90,100的竹竿子用下面的魔法獲得get

  1. 對長度爲98的竹竿子,使用2次延長魔法,使其長度達到100(消耗MP:2)
  2. 對長度爲30和40的竹竿子使用合成魔法,使其長度達到70(消耗MP:10)
  3. 對長度爲21的竹竿子使用縮短魔法,使其長度達到20
  4. 使用合成魔法合唱2獲得的70和3獲得的20,合成長度爲90的竹竿子

例2
輸入input

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

相關文章
相關標籤/搜索