2026-8-30拼多多笔试真题-哪个查阅窗能留空(C++/Py/Java /Js/Go)

哪个查阅窗能留空

拼多多 8月30号 笔试真题 第四题

拼多多真题目录点击查看: 拼多多 春招&秋招 笔试真题题库目录|笔试题库 + 算法考点详解

题目内容

档案室夜班要把当天的调档请求排进一排查阅窗。值班员必须先给自己留出一个空窗去核总账,再判断剩下的窗够不够分给所有调档员,并把每个窗能不能留空写进值班日志。

今晚共有 k k k 名调档员,查阅窗编号为 1 , 2 , ... , k + 1 1,2,\dots,k+1 1,2,...,k+1,一共 k + 1 k+1 k+1 个窗。第 i i i 名调档员只接受闭区间 p i , q i p_i,q_i pi,qi 里的整数编号窗。每名调档员必须恰好分到一个窗,同一个窗最多分给一名调档员。

对每个窗 t t t,请独立判断:如果窗 t t t 完全不分配、专门留给值班员,是否仍然能把全部 k k k 名调档员安排到各自能接受、且互不冲突的窗上。

请按窗号从小到大输出判断结果。判断不同的 t t t 时,可以采用完全不同的分配方案。

调档员人数满足 1 ≤ k ≤ 2 × 10 5 1 \le k \le 2 \times 10^5 1≤k≤2×105,且 1 ≤ p i ≤ q i ≤ k + 1 1 \le p_i \le q_i \le k+1 1≤pi≤qi≤k+1。

输入描述

第一行一个整数 k k k( 1 ≤ k ≤ 2 × 10 5 1 \le k \le 2 \times 10^5 1≤k≤2×105),表示调档员人数。

接下来 k k k 行,第 i i i 行两个整数 p i p_i pi、 q i q_i qi( 1 ≤ p i ≤ q i ≤ k + 1 1 \le p_i \le q_i \le k+1 1≤pi≤qi≤k+1),表示第 i i i 名调档员能接受的窗号闭区间。

输出描述

输出一个长度为 k + 1 k+1 k+1 的 01 字符串。第 t t t 个字符为 1,当且仅当存在一种合法安排,使全部调档员都分到窗、且窗 t t t 不分配给任何人;否则为 0

样例1

输入

复制代码
1
1 2

输出

复制代码
11

说明

只有一名调档员,两个窗都能接受。留空窗 1 时分到 2,留空窗 2 时分到 1,两种都可行。

样例2

输入

复制代码
3
1 4
2 2
3 3

输出

复制代码
1001

说明

  • 留空窗 1:三人可分别分到 423
  • 留空窗 2:第二人只接受窗 2,无法安排。
  • 留空窗 3:第三人只接受窗 3,无法安排。
  • 留空窗 4:三人可分别分到 123

样例3

输入

复制代码
3
1 2
1 2
2 2

输出

复制代码
0000

说明

三人都只能挤在窗 12 里,这两个窗本身就装不下三个人。无论留空哪一个窗,都无法安排,因此四个位置都是 0

题解

思路

解题思路: 贪心 + BFS + 线段树

  1. 贪心找出一个合法匹配。对调度员区间按照右端点升序,每个调度员尽可能分配较小位置。记录对应空闲窗口编号。
  2. 用线段树维护"哪些区间包含当前空窗"。
  3. 从初始空闲窗口进行BFS,通过交换不断扩展空窗的可达范围。如果当前空闲窗口为x,在线段树中找到包含该窗口的区间对应的调度员,交换它原先位置至x,那么他原先占据位置即可为空闲窗口。新的空闲窗口重新加入队列中。
  4. 算法总体时间复杂度为O(k log k)

C++

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

struct Node {
    int l, r, id;
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int k;
    cin >> k;
    int n = k + 1;

    vector<Node> a(k);
    for (int i = 0; i < k; i++) {
        cin >> a[i].l >> a[i].r;
        a[i].id = i;
    }

    // 按右端点排序,贪心寻找一个合法分配
    sort(a.begin(), a.end(), [](const Node& x, const Node& y) {
        return x.r < y.r;
    });

    set<int> s;
    for (int i = 1; i <= n; i++) s.insert(i);

    vector<int> match(k);

    for (auto &x : a) {
        auto it = s.lower_bound(x.l);
        if (it == s.end() || *it > x.r) {
            cout << string(n, '0');
            return 0;
        }
        match[x.id] = *it;
        s.erase(it);
    }

    // 唯一没有被分配的窗
    int start = *s.begin();

    vector<vector<int>> seg(4 * n);

    function<void(int,int,int,int,int,int)> add = [&](int p, int l, int r, int ql, int qr, int id) {
        if (ql <= l && r <= qr) {
            seg[p].push_back(id);
            return;
        }
        int mid = (l + r) / 2;
        if (ql <= mid) add(p * 2, l, mid, ql, qr, id);
        if (qr > mid) add(p * 2 + 1, mid + 1, r, ql, qr, id);
    };

    // 把每个调档员的可接受区间放入线段树
    for (int i = 0; i < k; i++)
        add(1, 1, n, a[i].l, a[i].r, a[i].id);

    vector<bool> vis(n + 1);
    vector<bool> used(k);
    queue<int> q;

    vis[start] = true;
    q.push(start);

    function<void(int,int,int,int)> query = [&](int p, int l, int r, int x) {
        for (int id : seg[p]) {
            if (used[id]) continue;
            used[id] = true;

            // 调档员 id 可以从原来的位置换到 x,
            // 因此原来的位置就变成新的空窗
            int y = match[id];

            if (!vis[y]) {
                vis[y] = true;
                q.push(y);
            }
        }

        if (l == r) return;

        int mid = (l + r) / 2;
        if (x <= mid)
            query(p * 2, l, mid, x);
        else
            query(p * 2 + 1, mid + 1, r, x);
    };

    while (!q.empty()) {
        int x = q.front();
        q.pop();
        query(1, 1, n, x);
    }

    for (int i = 1; i <= n; i++)
        cout << vis[i];

    return 0;
}

java

java 复制代码
import java.io.*;
import java.util.*;

public class Main {
    static class Node {
        int l, r, id;

        Node(int l, int r, int id) {
            this.l = l;
            this.r = r;
            this.id = id;
        }
    }

    static int n;
    static List<Integer>[] seg;
    static boolean[] vis;
    static boolean[] used;
    static int[] match;
    static Queue<Integer> q;

    static void add(int p, int l, int r, int ql, int qr, int id) {
        if (ql <= l && r <= qr) {
            seg[p].add(id);
            return;
        }

        int mid = (l + r) / 2;

        if (ql <= mid)
            add(p * 2, l, mid, ql, qr, id);

        if (qr > mid)
            add(p * 2 + 1, mid + 1, r, ql, qr, id);
    }

    static void query(int p, int l, int r, int x) {
        for (int id : seg[p]) {
            if (used[id])
                continue;

            used[id] = true;

            // 调档员 id 可以从原来的位置换到 x,
            // 因此原来的位置就变成新的空窗
            int y = match[id];

            if (!vis[y]) {
                vis[y] = true;
                q.offer(y);
            }
        }

        if (l == r)
            return;

        int mid = (l + r) / 2;

        if (x <= mid)
            query(p * 2, l, mid, x);
        else
            query(p * 2 + 1, mid + 1, r, x);
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(
                new InputStreamReader(System.in)
        );

        StringBuilder input = new StringBuilder();
        String line;

        while ((line = br.readLine()) != null) {
            input.append(line).append(' ');
        }

        StringTokenizer st = new StringTokenizer(input.toString());

        int k = Integer.parseInt(st.nextToken());
        n = k + 1;

        Node[] a = new Node[k];

        for (int i = 0; i < k; i++) {
            int l = Integer.parseInt(st.nextToken());
            int r = Integer.parseInt(st.nextToken());

            a[i] = new Node(l, r, i);
        }

        // 按右端点排序,贪心寻找一个合法分配
        Arrays.sort(a, new Comparator<Node>() {
            @Override
            public int compare(Node x, Node y) {
                return Integer.compare(x.r, y.r);
            }
        });

        TreeSet<Integer> s = new TreeSet<Integer>();

        for (int i = 1; i <= n; i++)
            s.add(i);

        int[] match = new int[k];
        Main.match = match;

        for (Node x : a) {
            Integer pos = s.ceiling(x.l);

            if (pos == null || pos > x.r) {
                StringBuilder ans = new StringBuilder();

                for (int i = 0; i < n; i++)
                    ans.append('0');

                System.out.println(ans);
                return;
            }

            match[x.id] = pos;
            s.remove(pos);
        }

        // 唯一没有被分配的窗
        int start = s.first();

        @SuppressWarnings("unchecked")
        List<Integer>[] tree = new ArrayList[4 * n];

        for (int i = 0; i < tree.length; i++)
            tree[i] = new ArrayList<Integer>();

        seg = tree;

        // 把每个调档员的可接受区间放入线段树
        for (int i = 0; i < k; i++) {
            add(
                1,
                1,
                n,
                a[i].l,
                a[i].r,
                a[i].id
            );
        }

        vis = new boolean[n + 1];
        used = new boolean[k];

        q = new ArrayDeque<Integer>();

        vis[start] = true;
        q.offer(start);

        while (!q.isEmpty()) {
            int x = q.poll();

            query(1, 1, n, x);
        }

        StringBuilder ans = new StringBuilder();

        for (int i = 1; i <= n; i++) {
            if (vis[i])
                ans.append('1');
            else
                ans.append('0');
        }

        System.out.println(ans);
    }
}

python

python 复制代码
import sys
import bisect
from collections import deque

input = sys.stdin.readline

k = int(input())
n = k + 1

a = []
for i in range(k):
    l, r = map(int, input().split())
    a.append([l, r, i])

# 按右端点排序,贪心寻找一个合法分配
a.sort(key=lambda x: x[1])

s = list(range(1, n + 1))
match = [0] * k

for l, r, idx in a:
    pos = bisect.bisect_left(s, l)
    if pos == len(s) or s[pos] > r:
        print('0' * n)
        sys.exit()

    match[idx] = s[pos]
    s.pop(pos)

# 唯一没有被分配的窗
start = s[0]

seg = [[] for _ in range(4 * n)]

def add(p, l, r, ql, qr, idx):
    if ql <= l and r <= qr:
        seg[p].append(idx)
        return

    mid = (l + r) // 2
    if ql <= mid:
        add(p * 2, l, mid, ql, qr, idx)
    if qr > mid:
        add(p * 2 + 1, mid + 1, r, ql, qr, idx)

# 把每个调档员的可接受区间放入线段树
for l, r, idx in a:
    add(1, 1, n, l, r, idx)

vis = [False] * (n + 1)
used = [False] * k
q = deque()

vis[start] = True
q.append(start)

def query(p, l, r, x):
    for idx in seg[p]:
        if used[idx]:
            continue

        used[idx] = True

        # 调档员 id 可以从原来的位置换到 x,
        # 因此原来的位置就变成新的空窗
        y = match[idx]

        if not vis[y]:
            vis[y] = True
            q.append(y)

    if l == r:
        return

    mid = (l + r) // 2
    if x <= mid:
        query(p * 2, l, mid, x)
    else:
        query(p * 2 + 1, mid + 1, r, x)

while q:
    x = q.popleft()
    query(1, 1, n, x)

print(''.join('1' if vis[i] else '0' for i in range(1, n + 1)))

javascript

js 复制代码
const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    crlfDelay: Infinity
});

const input = [];

rl.on('line', line => {
    input.push(...line.trim().split(/\s+/));
});

rl.on('close', () => {
    let idx = 0;

    const k = Number(input[idx++]);
    const n = k + 1;

    const a = [];
    for (let i = 0; i < k; i++) {
        const l = Number(input[idx++]);
        const r = Number(input[idx++]);
        a.push({ l, r, id: i });
    }

    // 按右端点排序,贪心寻找一个合法分配
    a.sort((x, y) => x.r - y.r);

    const s = [];
    for (let i = 1; i <= n; i++) s.push(i);

    const match = new Array(k).fill(0);

    for (const x of a) {
        let left = 0, right = s.length;

        while (left < right) {
            const mid = (left + right) >> 1;
            if (s[mid] >= x.l)
                right = mid;
            else
                left = mid + 1;
        }

        if (left === s.length || s[left] > x.r) {
            console.log('0'.repeat(n));
            return;
        }

        match[x.id] = s[left];
        s.splice(left, 1);
    }

    // 唯一没有被分配的窗
    const start = s[0];

    const seg = Array.from({ length: 4 * n }, () => []);

    function add(p, l, r, ql, qr, id) {
        if (ql <= l && r <= qr) {
            seg[p].push(id);
            return;
        }

        const mid = (l + r) >> 1;
        if (ql <= mid)
            add(p * 2, l, mid, ql, qr, id);
        if (qr > mid)
            add(p * 2 + 1, mid + 1, r, ql, qr, id);
    }

    // 把每个调档员的可接受区间放入线段树
    for (const x of a)
        add(1, 1, n, x.l, x.r, x.id);

    const vis = new Array(n + 1).fill(false);
    const used = new Array(k).fill(false);
    const q = [];

    vis[start] = true;
    q.push(start);

    function query(p, l, r, x) {
        for (const id of seg[p]) {
            if (used[id]) continue;

            used[id] = true;

            // 调档员 id 可以从原来的位置换到 x,
            // 因此原来的位置就变成新的空窗
            const y = match[id];

            if (!vis[y]) {
                vis[y] = true;
                q.push(y);
            }
        }

        if (l === r) return;

        const mid = (l + r) >> 1;
        if (x <= mid)
            query(p * 2, l, mid, x);
        else
            query(p * 2 + 1, mid + 1, r, x);
    }

    let head = 0;
    while (head < q.length) {
        const x = q[head++];
        query(1, 1, n, x);
    }

    let ans = '';
    for (let i = 1; i <= n; i++)
        ans += vis[i] ? '1' : '0';

    console.log(ans);
});

Go

go 复制代码
package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
)

type Node struct {
	l, r, id int
}

var n int
var seg [][]int
var vis []bool
var used []bool
var match []int
var q []int

func add(p, l, r, ql, qr, id int) {
	if ql <= l && r <= qr {
		seg[p] = append(seg[p], id)
		return
	}

	mid := (l + r) / 2
	if ql <= mid {
		add(p*2, l, mid, ql, qr, id)
	}
	if qr > mid {
		add(p*2+1, mid+1, r, ql, qr, id)
	}
}

func query(p, l, r, x int) {
	for _, id := range seg[p] {
		if used[id] {
			continue
		}

		used[id] = true

		// 调档员 id 可以从原来的位置换到 x,
		// 因此原来的位置就变成新的空窗
		y := match[id]

		if !vis[y] {
			vis[y] = true
			q = append(q, y)
		}
	}

	if l == r {
		return
	}

	mid := (l + r) / 2
	if x <= mid {
		query(p*2, l, mid, x)
	} else {
		query(p*2+1, mid+1, r, x)
	}
}

func main() {
	in := bufio.NewReader(os.Stdin)
	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()

	var k int
	fmt.Fscan(in, &k)

	n = k + 1

	a := make([]Node, k)
	for i := 0; i < k; i++ {
		fmt.Fscan(in, &a[i].l, &a[i].r)
		a[i].id = i
	}

	// 按右端点排序,贪心寻找一个合法分配
	sort.Slice(a, func(i, j int) bool {
		return a[i].r < a[j].r
	})

	s := make([]int, n)
	for i := 0; i < n; i++ {
		s[i] = i + 1
	}

	match = make([]int, k)

	for _, x := range a {
		pos := sort.SearchInts(s, x.l)

		if pos == len(s) || s[pos] > x.r {
			for i := 0; i < n; i++ {
				fmt.Fprint(out, "0")
			}
			return
		}

		match[x.id] = s[pos]
		s = append(s[:pos], s[pos+1:]...)
	}

	// 唯一没有被分配的窗
	start := s[0]

	seg = make([][]int, 4*n)

	// 把每个调档员的可接受区间放入线段树
	for _, x := range a {
		add(1, 1, n, x.l, x.r, x.id)
	}

	vis = make([]bool, n+1)
	used = make([]bool, k)
	q = make([]int, 0)

	vis[start] = true
	q = append(q, start)

	head := 0
	for head < len(q) {
		x := q[head]
		head++

		query(1, 1, n, x)
	}

	for i := 1; i <= n; i++ {
		if vis[i] {
			fmt.Fprint(out, "1")
		} else {
			fmt.Fprint(out, "0")
		}
	}
}
相关推荐
luj_176835 分钟前
毒素驱动的生物自毁机制探析
服务器·c语言·开发语言·经验分享·算法
一水鉴天1 小时前
问题分类的三元组映射体系与求解方法论 20260901(千问)
人工智能·算法·机器学习
breeze jiang1 小时前
LangChain Memory 实战:用 InMemoryChatMessageHistory 管理多轮对话
前端·算法·langchain
地平线开发者16 小时前
【模型轻量化专题】衡量模型轻量性的指标
算法
学习星球19 小时前
OFDM技术精讲:正交性推导、循环前缀原理与80行Python链路仿真(附实测数据)
算法·面试·前端框架
alphaTao1 天前
LeetCode 每日一题 2026/8/24-2026/8/30
python·算法·leetcode
Interview Aid1121 天前
TikTok OA 四题分享|半小时内 AC,题目基本都是实现题
java·开发语言·算法·面试·职场和发展
顶点多余1 天前
那些在算法中适合巩固的知识点---1
java·前端·算法
罗西的思考2 天前
【Agentic RL / 强化学习框架】Molt 设计解读
人工智能·算法·机器学习