บล็อกพาร์ทิชันสตริง


11

แรงบันดาลใจ

พิจารณารายการlประกอบด้วยตัวเลข กำหนดดำเนินการบล็อกที่ดัชนีiอยู่ในรายชื่อlที่จะทำหน้าที่ในการเคลื่อนย้าย 3 องค์ประกอบติดต่อกันเริ่มต้นจากiในlไปยังจุดสิ้นสุด

ตัวอย่าง:

l, i (1-indexing) -> l (after applying block operation at index i)
[1,2,3,4,5], 1 -> [4,5,1,2,3]
[1,2,3,4,5,6,7], 3 -> [1,2,6,7,3,4,5]

รับรายการที่ประกอบด้วยเพียง 0 และ 1 ความท้าทายของคุณคือการแบ่งพาร์ติชันเพื่อให้ศูนย์อยู่ข้างหน้าและรายการอยู่ด้านหลังโดยใช้การดำเนินการบล็อกเท่านั้น ผลลัพธ์ควรเป็นดัชนีตามลำดับที่ใช้กับรายการ

เนื่องจากรายการนี้เป็นไปไม่ได้สำหรับ[1,0,1,0]ความยาวของรายการจึงรับประกันได้ว่าจะมีอย่างน้อย 5 รายการ

กรณีทดสอบ (การจัดทำดัชนี 1 รายการ)

(มีเอาต์พุตที่ถูกต้องอื่น ๆ )

[1,1,1,0,0] -> [1]
[0,1,0,1,0] -> [1,2,1,1]
[0,0,0,1,1,1,0,0,0] -> [4]

ใช้สคริปต์นี้เพื่อสร้างกรณีทดสอบเพิ่มเติม (input เท่านั้น. the rplc ' ';','ส่วนหนึ่งจะใช้ในการRอีพีพื้นที่จด้วยเครื่องหมายจุลภาคในการส่งออก)

เกณฑ์การชนะ

เป็นเกณฑ์การชนะหลักและคือ tie-breaker โดยเฉพาะอย่างยิ่ง:

  • โซลูชันที่มีความยาวผลลัพธ์ที่สั้นที่สุด (จำนวนการดำเนินการบล็อกน้อยที่สุด) กับกรณีทดสอบ ( n_elem= 500, random_seed= {secret value}) ชนะ คุณควรจะสามารถเรียกใช้โซลูชันของคุณให้เสร็จสมบูรณ์ด้วยกรณีทดสอบ ( n_elem= 500, random_seed= 123456)
  • ในกรณีของความสัมพันธ์โซลูชันที่สามารถจัดการค่า power-of-2 ที่ใหญ่ที่สุดของn_elemด้วยrandom_seed= {secret value} ภายใน 10 วินาที (สำหรับฉัน) ชนะ
  • ในกรณีของความสัมพันธ์โซลูชันที่ใช้เวลาน้อยลงในกรณีทดสอบนั้นจะชนะ

โพสต์ Sandbox (หมายเหตุ) ฉันมีวิธีแก้ปัญหาพื้นที่เชิงเส้นเวลาเชิงเส้น แต่มีปัจจัยคงที่ขนาดใหญ่นอกเหนือจากมันไม่ง่ายที่จะใช้ เป็นไปได้ที่จะลดปัจจัยคงที่ แต่ก็ยากที่จะนำไปใช้
user202729

(ข้อจำกัดความรับผิดชอบ: ฉันได้แก้ไขความท้าทายที่เชื่อมโยงแล้ว)
user202729

เพียงเพื่อชี้แจงผลลัพธ์ไม่จำเป็นต้องเป็นผลลัพธ์ที่สั้นที่สุด?
JungHwan Min

@JungHwanMin ถูกต้อง
user202729

การดำเนินการตรวจสอบใน C ++ โดยใช้เชือก การดำเนินไร้เดียงสาโดยรั่ว
user202729

คำตอบ:


8

Python 3 , (0.397 n + 3.58) ขั้นตอน

1000 numpy.polyfitจุดถดถอยพหุนามโดย


  • จำนวนขั้นตอนสำหรับเวอร์ชัน 1: 0.0546 n² + 2.80 n - 221
  • จำนวนขั้นตอนสำหรับเวอร์ชัน 2: 0.0235 n² + 0.965 n - 74
  • จำนวนขั้นตอนสำหรับเวอร์ชัน 3: 0.00965 n² + 2.35 n - 111
  • จำนวนขั้นตอนสำหรับเวอร์ชัน 4: 1.08 n - 36.3
  • จำนวนขั้นตอนสำหรับเวอร์ชั่น 5: 0.397 n + 3.58

  • คะแนนกรณีทดสอบลับสำหรับรุ่น 1: 14468
  • คะแนนกรณีทดสอบลับสำหรับรุ่น 2: 5349
  • คะแนนกรณีทดสอบลับสำหรับรุ่น 3: 4143
  • คะแนนกรณีทดสอบลับสำหรับรุ่น 4: 450
  • คะแนนกรณีทดสอบลับสำหรับรุ่น 5: 205

def partite(L):
	endgame5 = [9,9,1,9,0,0,1,9,
		0,1,0,1,0,1,1,9,
		0,0,1,0,0,0,1,0,
		0,0,0,1,0,0,0,9]
	endgame6 = [9,9,2,9,1,1,2,9,0,2,0,0,1,2,2,9,
		0,1,2,1,0,1,2,1,0,1,0,2,1,1,0,9,
		0,0,1,0,1,0,1,1,0,0,0,0,1,1,1,1,
		0,0,2,2,0,0,2,2,0,0,0,0,0,0,0,9]
	endgame = [9,9,3,9,2,2,3,9,1,0,3,0,2,0,3,9,0,1,3,3,2,2,3,0,1,0,1,0,2,1,0,9,
		0,0,2,1,0,0,2,2,1,0,1,2,0,0,0,2,0,1,3,3,3,3,3,0,1,1,1,1,1,3,0,9,
		0,0,0,0,1,0,1,1,1,0,3,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,0,2,0,1,0,
		0,0,2,0,0,0,2,0,0,0,2,0,0,0,2,0,0,0,3,0,3,0,3,0,3,0,2,3,3,0,0,9]
	offset = 1
	steps = []
	def update(L,steps,ind):
		steps.append(offset + ind)
		if 0 <= ind and ind+3 < len(L):
			return (steps,L[:ind]+L[ind+3:]+L[ind:ind+3])
		else:
			print(offset,ind,L)
			raise
	if len(L) == 5:
		while endgame5[L[0]*16+L[1]*8+L[2]*4+L[3]*2+L[4]] != 9:
			steps, L = update(L,steps,endgame5[L[0]*16+L[1]*8+L[2]*4+L[3]*2+L[4]])
		return steps
	if len(L) == 6:
		while endgame6[L[0]*32+L[1]*16+L[2]*8+L[3]*4+L[4]*2+L[5]] != 9:
			steps, L = update(L,steps,endgame6[L[0]*32+L[1]*16+L[2]*8+L[3]*4+L[4]*2+L[5]])
		return steps
	if 1 not in L:
		return []
	while len(L) > 7 and 0 in L:
		wf_check = len(L)
		while L[0] != 0:
			pos = [-1]
			wf_check2 = -1
			while True:
				i = pos[-1]+1
				while i < len(L):
					if L[i] == 0:
						pos.append(i)
						i += 1
					else:
						i += 3
				assert len(pos) > wf_check2
				wf_check2 = len(pos)
				space = (pos[-1]-len(L)+1)%3
				ind = -1
				tail = pos.pop()
				i = len(L)-1
				while i >= 0:
					if tail == i:
						while tail == i:
							i -= 1
							tail = pos.pop() if pos else -1
						i -= 2
					elif i < len(L)-3 and L[i+space] == 0:
						ind = i
						break
					else:
						i -= 1
				if ind == -1:
					break
				steps, L = update(L, steps, ind)
				pos = pos or [-1]
			if L[0] == 0:
				break
			pos = [-1]
			while L[0] != 0:
				pos = [-1]
				found = False
				for i in range(1,len(L)):
					if L[i] == 0:
						if i%3 == (pos[-1]+1)%3:
							pos.append(i)
						else:
							found = found or i
				if found > len(L)-4:
					found = False
				if not found:
					break
				triple = []
				for i in range(1,len(L)-1):
					if L[i-1] == 1 and L[i] == 1 and L[i+1] == 1:
						triple.append(i)
					if len(triple) > 3:
						break
				space = (pos[-1]-len(L)+1)%3
				if space == 0:
					if found >= 2 and found-2 not in pos and found-1 not in pos:
						# ... _ 1 _ [0] 0 ...
						if found-2 in triple:
							triple.remove(found-2)
						if found-3 in triple:
							triple.remove(found-3)
						if L[-1] == 1:
							steps, L = update(L, steps, found-2)
							steps, L = update(L, steps, len(L)-4)
							steps, L = update(L, steps, len(L)-4)
						elif triple:
							steps, L = update(L, steps, found-2)
							if found < triple[0]:
								triple[0] -= 3
							steps, L = update(L, steps, triple[0]-1)
							steps, L = update(L, steps, len(L)-4)
						else:
							break
						assert L[-3] == 0
					elif found >= 1 and found-1 not in pos and found+1 not in pos:
						# ... _ 1 [0] _ 0 ...
						if found-2 in triple:
							triple.remove(found-2)
						if L[-2] == 1 and L[-1] == 1:
							steps, L = update(L, steps, found-1)
							steps, L = update(L, steps, len(L)-5)
							steps, L = update(L, steps, len(L)-5)
						elif triple:
							steps, L = update(L, steps, found-1)
							if found < triple[0]:
								triple[0] -= 3
							steps, L = update(L, steps, triple[0]-1)
							steps, L = update(L, steps, len(L)-5)
						elif L[-1] == 1:
							steps, L = update(L, steps, found-1)
							steps, L = update(L, steps, len(L)-4)
							steps, L = update(L, steps, len(L)-4)
							steps, L = update(L, steps, len(L)-4)
						else:
							break
						assert L[-3] == 0
					else:
						break
				elif space == 1:
					# ... 1 1 [0] 0 ...
					if found >= 2 and found-2 not in pos and found-1 not in pos:
						if found-2 in triple:
							triple.remove(found-2)
						if found-3 in triple:
							triple.remove(found-3)
						if triple:
							steps, L = update(L, steps, found-2)
							if found < triple[0]:
								triple[0] -= 3
							steps, L = update(L, steps, triple[0]-1)
							steps, L = update(L, steps, len(L)-5)
						elif L[-2] == 1 and L[-1] == 1:
							steps, L = update(L, steps, found-2)
							steps, L = update(L, steps, len(L)-4)
							steps, L = update(L, steps, len(L)-5)
						else:
							break
						assert L[-2] == 0
					else:
						break
				else:
					if found+1 not in pos and found+2 not in pos:
						# ... 0 [0] _ 1 _ ...
						if found+2 in triple:
							triple.remove(found+2)
						if found+3 in triple:
							triple.remove(found+3)
						if L[-2] == 1 and L[-1] == 1:
							steps, L = update(L, steps, found)
							steps, L = update(L, steps, len(L)-5)
						elif L[-1] == 1:
							steps, L = update(L, steps, found)
							steps, L = update(L, steps, len(L)-4)
							steps, L = update(L, steps, len(L)-4)
						elif triple:
							steps, L = update(L, steps, triple[0]-1)
							if triple[0] < found:
								found -= 3
							steps, L = update(L, steps, found)
							steps, L = update(L, steps, len(L)-5)
						else:
							break
						assert L[-1] == 0
					elif found >= 1 and found-1 not in pos and found+1 not in pos:
						# ... 0 _ [0] 1 _ ...
						if found+2 in triple:
							triple.remove(found+2)
						if L[-1] == 1:
							steps, L = update(L, steps, found-1)
							steps, L = update(L, steps, len(L)-4)
						elif triple:
							steps, L = update(L, steps, triple[0]-1)
							if triple[0] < found:
								found -= 3
							steps, L = update(L, steps, found-1)
							steps, L = update(L, steps, len(L)-4)
						else:
							break
						assert L[-1] == 0
					else:
						break
			if L[0] == 0:
				break
			if 0 in L[::3]:
				assert L[::3].index(0) < wf_check
				wf_check = L[::3].index(0)
			steps, L = update(L, steps, 0)
		assert L[0] == 0
		offset += L.index(1)
		del L[:L.index(1)]
		continue
	if 0 in L:
		offset -= 7-len(L)
		L = [0]*(7-len(L))+L
		assert(len(L) == 7)
		while endgame[L[0]*64+L[1]*32+L[2]*16+L[3]*8+L[4]*4+L[5]*2+L[6]] != 9:
			steps, L = update(L,steps,endgame[L[0]*64+L[1]*32+L[2]*16+L[3]*8+L[4]*4+L[5]*2+L[6]])
	return steps

ลองออนไลน์!


3

Python 3 ~ 179 ขั้นตอนสำหรับ n = 500 (โดยเฉลี่ย)

วิธีการแก้ปัญหาโลภ ค่อนข้างช้า แต่ก็ใช้ได้ดี ใช้ตัวแก้ปัญหาที่ดีที่สุดสำหรับขนาดเล็ก

def incomplete_groups(l):
    r = 0
    ones = 0
    for x in l:
        if x == "1":
            ones += 1
        else:
            if ones % 3:
                r += 1
            ones = 0
    # Ones at the end don't count as an incomplete group.

    return r

def move(l, i):
    return l[:i] + l[i+3:] + l[i:i+3]

def best_pos(l, hist):
    r = []
    cleanup = incomplete_groups(l) == 0

    candidates = []
    for i in range(len(l) - 3):
        block = l[i:i+3]
        if block == "111" and cleanup:
            return i
        elif block == "111":
            continue

        new = move(l, i)
        bad_start = i < 3 and "10" in l[:3]
        candidates.append((new not in hist, -incomplete_groups(new), bad_start, block != "000", i))

    candidates.sort(reverse=True)
    return candidates[0][-1]

def done(l):
    return list(l) == sorted(l)



class IDAStar:
    def __init__(self, h, neighbours):
        """ Iterative-deepening A* search.

        h(n) is the heuristic that gives the cost between node n and the goal node. It must be admissable, meaning that h(n) MUST NEVER OVERSTIMATE the true cost. Underestimating is fine.

        neighbours(n) is an iterable giving a pair (cost, node, descr) for each node neighbouring n
        IN ASCENDING ORDER OF COST. descr is not used in the computation but can be used to
        efficiently store information about the path edges (e.g. up/left/right/down for grids).
        """

        self.h = h
        self.neighbours = neighbours
        self.FOUND = object()


    def solve(self, root, is_goal, max_cost=None):
        """ Returns the shortest path between the root and a given goal, as well as the total cost.
        If the cost exceeds a given max_cost, the function returns None. If you do not give a
        maximum cost the solver will never return for unsolvable instances."""

        self.is_goal = is_goal
        self.path = [root]
        self.is_in_path = {root}
        self.path_descrs = []
        self.nodes_evaluated = 0

        bound = self.h(root)

        while True:
            t = self._search(0, bound)
            if t is self.FOUND: return self.path, self.path_descrs, bound, self.nodes_evaluated
            if t is None: return None
            bound = t

    def _search(self, g, bound):
        self.nodes_evaluated += 1

        node = self.path[-1]
        f = g + self.h(node)
        if f > bound: return f
        if self.is_goal(node): return self.FOUND

        m = None # Lower bound on cost.
        for cost, n, descr in self.neighbours(node):
            if n in self.is_in_path: continue

            self.path.append(n)
            self.is_in_path.add(n)
            self.path_descrs.append(descr)
            t = self._search(g + cost, bound)

            if t == self.FOUND: return self.FOUND
            if m is None or (t is not None and t < m): m = t

            self.path.pop()
            self.path_descrs.pop()
            self.is_in_path.remove(n)

        return m

def h(l):
    """Number of groups of 1 with length <= 3 that come before a zero."""
    h = 0
    num_ones = 0
    complete_groups = 0
    incomplete_groups = 0
    for x in l:
        if x == "1":
            num_ones += 1
        else:
            while num_ones > 3:
                num_ones -= 3
                h += 1
                complete_groups += 1
            if num_ones > 0:
                h += 1
                incomplete_groups += 1
            num_ones = 0

    return complete_groups + incomplete_groups

def neighbours(l):
    inc_groups = incomplete_groups(l)
    final = inc_groups == 0

    candidates = []
    for i in range(len(l) - 3):
        left = l[:i]
        block = l[i:i+3]
        right = l[i+3:]
        cand = (1, left + right + block, i)

        # Optimal choice.
        if final and (block != "111" or i >= len(l.rstrip("1"))):
            continue

        candidates.append(cand)

    candidates.sort(key=lambda c: c[2], reverse=True)

    return candidates


def is_goal(l):
    return all(l[i] <= l[i+1] for i in range(len(l)-1))

opt_solver = IDAStar(h, neighbours)

def partite(l):
    if isinstance(l, list):
        l = "".join(map(str, l))
    if len(l) < 10:
        return [i + 1 for i in opt_solver.solve(l, is_goal)[1]]
    moves = []
    hist = [l]
    while not done(l):
        i = best_pos(l, hist)
        l = move(l, i)
        moves.append(i+1)
        hist.append(l)
    return moves
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.