绳段拆分判定
拼多多 10月26号 笔试真题 第二题
拼多多真题目录点击查看: 拼多多笔试真题题库目录|笔试题库 + 算法考点详解
题目内容
多多手里握着一根长度为 S S S 的绳子。
这根绳子允许切任意多刀,也允许保持原样、一刀都不动。每一次下刀,都是挑出当前还留着的某一截,把它的长度记作 y y y,再顺着正中分开,得到 ⌊ y 2 ⌋ \left\lfloor \frac{y}{2} \right\rfloor ⌊2y⌋ 与 ⌈ y 2 ⌉ \left\lceil \frac{y}{2} \right\rceil ⌈2y⌉ 这两截;前一个记号会把小数部分丢掉,后一个记号只要出现小数就往前进一位。长度已经是 1 1 1 的那一截,没法再分开。
希望最后桌上正好摆着 K K K 截,各截长度分别是 B 1 , B 2 , ... , B K B_1,B_2,\dots,B_K B1,B2,...,BK。每一截都得是切出来的一整根,不能拿几截重新接上;桌上也不许多留任何一截。
请判断多多只靠这样切,能不能凑出这 K K K 截。
输入描述
首行给出整数 Q Q Q( 0 < Q ≤ 50 0 < Q \le 50 0<Q≤50),表示数据一共有几组。
每组占两行。前一行是两个整数 S , K S,K S,K( 1 ≤ S ≤ 10 14 , 1 ≤ K ≤ 10 5 1 \le S \le 10^{14},1 \le K \le 10^5 1≤S≤1014,1≤K≤105),依次表示绳子一开始有多长、想切成几截;后一行是 K K K 个整数 B 1 , B 2 , ... , B K B_1,B_2,\dots,B_K B1,B2,...,BK( 1 ≤ B i ≤ 10 9 1 \le B_i \le 10^9 1≤Bi≤109),依次表示每一截想要的长度。
输出描述
一共写出 Q Q Q 行。每一行只写 Y E S YES YES 或 N O NO NO,表示这一组能不能切成目标绳子。
样例1
输入
3
7 3
2 2 3
6 3
2 2 2
9 2
4 6
输出
YES
NO
NO
说明
- 第一组:长度为 7 7 7 的绳子先分开成 3 3 3 与 4 4 4,再把长度为 4 4 4 的那截分开成 2 2 2 与 2 2 2,桌上就是 2 , 2 , 3 2,2,3 2,2,3。
- 第二组:三截加起来等于 6 6 6,和一开始的长度一样,可是 6 6 6 只能先分开成 3 3 3 与 3 3 3;再动其中一截 3 3 3,只能得到 1 1 1 与 2 2 2,结果变成 3 , 1 , 2 3,1,2 3,1,2,永远变不成 2 , 2 , 2 2,2,2 2,2,2。
- 第三组: 4 4 4 与 6 6 6 加起来是 10 10 10,并不是一开始的 9 9 9。切割改变不了各截长度之和,因此做不到。
题解
思路
解题算法:贪心
- 由于S最大为10^14,所以不能真的模拟切割过程,采用
贪心 + 最大堆进行处理。 - 因为切割只能让绳子变短。如果当前最大的绳子都小于某个目标长度,那么这个目标长度就不可能再出现。所以每次优先切割当前最长的绳子,直到得到目标长度。
- 具体做法:
- 计算所有目标长度总和
sum, 如果sum !=S直接输出NO - 将所有目标长度放入一个最大堆
- 将S放入另一个最大堆
- 每次取出两个堆最大长度进行比较
- 如果相等,说明找到一段目标绳子,两个堆顶元素弹出。
- 如果当前绳子长度更大,就将它切成两段,把两段放回绳子堆
- 如果目标长度更大,说明无法得到目标,输出NO
- 计算所有目标长度总和
- 算法总体时间复杂度为
O(NlogN)
C++
cpp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int Q;
cin >> Q;
while (Q--) {
ll S, sum = 0;
int K;
cin >> S >> K;
priority_queue<ll> rope, target;
rope.push(S);
for (int i = 0; i < K; i++) {
ll b;
cin >> b;
sum += b;
target.push(b);
}
if (sum != S) {
cout << "NO\n";
continue;
}
bool ok = true;
while (!target.empty()) {
if (rope.empty()) {
ok = false;
break;
}
ll x = rope.top();
ll y = target.top();
if (x < y) {
ok = false;
break;
}
if (x == y) {
rope.pop();
target.pop();
} else {
rope.pop();
ll a = x / 2;
ll b = x - a;
if (a > 0) rope.push(a);
if (b > 0) rope.push(b);
}
}
cout << (ok ? "YES" : "NO") << '\n';
}
return 0;
}
java
java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int Q = sc.nextInt();
while (Q-- > 0) {
long S = sc.nextLong();
int K = sc.nextInt();
long sum = 0;
// 最大堆,Java 默认是最小堆,使用 Collections.reverseOrder()
PriorityQueue<Long> rope = new PriorityQueue<>(Collections.reverseOrder());
PriorityQueue<Long> target = new PriorityQueue<>(Collections.reverseOrder());
rope.offer(S);
for (int i = 0; i < K; i++) {
long b = sc.nextLong();
sum += b;
target.offer(b);
}
if (sum != S) {
System.out.println("NO");
continue;
}
boolean ok = true;
while (!target.isEmpty()) {
if (rope.isEmpty()) {
ok = false;
break;
}
long x = rope.peek();
long y = target.peek();
if (x < y) {
ok = false;
break;
}
if (x == y) {
rope.poll();
target.poll();
} else {
rope.poll();
long a = x / 2;
long b = x - a;
if (a > 0) rope.offer(a);
if (b > 0) rope.offer(b);
}
}
System.out.println(ok ? "YES" : "NO");
}
sc.close();
}
}
python
python
import heapq
Q = int(input())
for _ in range(Q):
S, K = map(int, input().split())
B = list(map(int, input().split()))
# 使用负数模拟最大堆
rope = [-S]
target = [-b for b in B]
heapq.heapify(target)
if sum(B) != S:
print("NO")
continue
ok = True
while target:
if not rope:
ok = False
break
x = -rope[0]
y = -target[0]
if x < y:
ok = False
break
if x == y:
heapq.heappop(rope)
heapq.heappop(target)
else:
heapq.heappop(rope)
a = x // 2
b = x - a
if a > 0:
heapq.heappush(rope, -a)
if b > 0:
heapq.heappush(rope, -b)
print("YES" if ok else "NO")
Go
go
package main
import (
"bufio"
"container/heap"
"fmt"
"os"
)
type MaxHeap []int64
func (h MaxHeap) Len() int {
return len(h)
}
func (h MaxHeap) Less(i, j int) bool {
return h[i] > h[j]
}
func (h MaxHeap) Swap(i, j int) {
h[i], h[j] = h[j], h[i]
}
func (h *MaxHeap) Push(x interface{}) {
*h = append(*h, x.(int64))
}
func (h *MaxHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[:n-1]
return x
}
func main() {
reader := bufio.NewReader(os.Stdin)
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
var Q int
fmt.Fscan(reader, &Q)
for ; Q > 0; Q-- {
var S int64
var K int
fmt.Fscan(reader, &S, &K)
rope := &MaxHeap{}
target := &MaxHeap{}
heap.Init(rope)
heap.Init(target)
heap.Push(rope, S)
var sum int64
for i := 0; i < K; i++ {
var b int64
fmt.Fscan(reader, &b)
sum += b
heap.Push(target, b)
}
if sum != S {
fmt.Fprintln(writer, "NO")
continue
}
ok := true
for target.Len() > 0 {
if rope.Len() == 0 {
ok = false
break
}
x := (*rope)[0]
y := (*target)[0]
if x < y {
ok = false
break
}
if x == y {
heap.Pop(rope)
heap.Pop(target)
} else {
heap.Pop(rope)
a := x / 2
b := x - a
if a > 0 {
heap.Push(rope, a)
}
if b > 0 {
heap.Push(rope, b)
}
}
}
if ok {
fmt.Fprintln(writer, "YES")
} else {
fmt.Fprintln(writer, "NO")
}
}
}
javascript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let input = [];
rl.on('line', line => {
input.push(...line.trim().split(/\s+/).filter(Boolean));
});
rl.on('close', () => {
let idx = 0;
const Q = Number(input[idx++]);
const ans = [];
// 最大堆实现
class MaxHeap {
constructor() {
this.heap = [];
}
size() {
return this.heap.length;
}
peek() {
return this.heap[0];
}
push(x) {
const h = this.heap;
h.push(x);
let i = h.length - 1;
while (i > 0) {
const p = Math.floor((i - 1) / 2);
if (h[p] >= h[i]) break;
[h[p], h[i]] = [h[i], h[p]];
i = p;
}
}
pop() {
const h = this.heap;
const res = h[0];
const last = h.pop();
if (h.length > 0) {
h[0] = last;
let i = 0;
while (true) {
let largest = i;
const l = i * 2 + 1;
const r = i * 2 + 2;
if (l < h.length && h[l] > h[largest]) {
largest = l;
}
if (r < h.length && h[r] > h[largest]) {
largest = r;
}
if (largest === i) break;
[h[i], h[largest]] = [h[largest], h[i]];
i = largest;
}
}
return res;
}
}
while (idx < input.length && ans.length < Q) {
const S = Number(input[idx++]);
const K = Number(input[idx++]);
let sum = 0;
const rope = new MaxHeap();
const target = new MaxHeap();
rope.push(S);
for (let i = 0; i < K; i++) {
const b = Number(input[idx++]);
sum += b;
target.push(b);
}
if (sum !== S) {
ans.push("NO");
continue;
}
let ok = true;
while (target.size() > 0) {
if (rope.size() === 0) {
ok = false;
break;
}
const x = rope.peek();
const y = target.peek();
if (x < y) {
ok = false;
break;
}
if (x === y) {
rope.pop();
target.pop();
} else {
rope.pop();
const a = Math.floor(x / 2);
const b = x - a;
if (a > 0) rope.push(a);
if (b > 0) rope.push(b);
}
}
ans.push(ok ? "YES" : "NO");
}
console.log(ans.join('\n'));
});