Huawei OD computer-based test algorithm questions: Minimum number of line segments to cover

Table of contents

Question part

Interpretation and analysis

Code


Question part

topic Minimum number of line segments to cover
difficulty Disaster
Question description Given a set of line segments on the coordinate axis (one-dimensional coordinate axis), the starting point and end point of the line segment are both integers and the length is not less than 1. Please find the minimum number of line segments that can cover all line segments.
Enter description The first line of input is the number of all line segments, not exceeding 10,000. Each subsequent line represents a line segment in the format of "x,y", x and y represent the starting point and end point respectively, and the value range is [-, 10^{5}] 10^{5}.
Output description The minimum number of line segments, which is a positive integer.
Additional information none
------------------------------------------------------
Example
Example 1
enter 3
1,4
2,5

3,6
output 2
illustrate Select 2 line segments [1,4] and [3,6], which can cover [2,5].


Interpretation and analysis

Question interpretation :

In a one-dimensional coordinate system, there are many line segments (identified by starting and ending points). In addition to finding the range covered by these line segments, it is necessary to find at least a few of these line segments that can cover these ranges.

Analysis and ideas :

Before answering this question, let's clarify that the range covered by these line segments may be discontinuous intervals. For example, the intervals covered by three line segments [1,4], [2,5], [6,7] are [1,5], [6,7].

There are three steps to answer this question: sorting, segmentation, and backtracking.
1. Sort. Sort all line segments. The sorting rule is to sort the starting points of the line segments first, from small to large; if the starting points are equal, then sort the end points, also from small to large.
2. Segmentation. As mentioned earlier, the range covered by these line segments may be discontinuous intervals. The so-called segmentation is to find these discontinuous intervals.
Traverse the line segments one by one. If the starting point of the next line segment is greater than the end point of the previous line segment, then the next line segment is in a different interval from the previous line segment; otherwise, the next line segment must be in the same interval as the migration line segment.
All line segments located in different intervals will never intersect. Therefore, the line segments in different segmented intervals can be counted separately, and finally the sum of the line segments in all intervals can be added.
3. Backtrack. For each individual interval, use the backtracking algorithm to exhaustively find all possible coverage situations, and find the situation with the smallest required line segment from all situations. Backtracking can be implemented using recursion, which is easy to understand.

The time complexity of this question is O( n^{2}) and the space complexity is O(n).


Code

Java code

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;

/**
 * 最少线段覆盖
 * 
 * @since 2023.09.23
 * @version 0.1
 * @author Frank
 *
 */
public class MinLineCount {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while (sc.hasNext()) {
			String input = sc.nextLine();
			int count = Integer.parseInt(input);

			List<int[]> lines = new ArrayList<>();
			for (int i = 0; i < count; i++) {
				input = sc.nextLine();
				String[] strStartEnd = input.split(",");
				int[] startEnd = new int[2];
				startEnd[0] = Integer.parseInt(strStartEnd[0]);
				startEnd[1] = Integer.parseInt(strStartEnd[1]);
				lines.add(startEnd);
			}
			processMinLineCount( lines);
		}
	}

	private static void processMinLineCount( List<int[]> lines) {
		// 1. 排序
		Comparator<int[]> cmp = new Comparator<int[]>() {
			@Override
			public int compare(int[] o1, int[] o2) {
				for (int i = 0; i < o1.length; i++) {
					if (o1[i] != o2[i]) {
						return o1[i] - o2[i];
					}
				}
				return 0;
			}
		};

		lines.sort(cmp);

		// 2. 分段
		class LinePart {
			int start; // 起始点,包含在内
			int end; // 终止点,包含在内
			int startIdx; // 对应lines中的起始下标,包含在内
			int endIdx; // 对应lines中的终止下标,包含在内
		}

		List<LinePart> lpList = new ArrayList<LinePart>();
		LinePart tmpLP = new LinePart();
		for (int i = 0; i < lines.size(); i++) {
			int[] tmpLine = lines.get(i);
			if (i == 0) {
				tmpLP.start = tmpLine[0];
				tmpLP.end = tmpLine[1];
				tmpLP.startIdx = 0;
				tmpLP.endIdx = 0;
				lpList.add( tmpLP );
				continue;
			}
			
			// 不是同一个区间
			if( tmpLine[0] > tmpLP.end )
			{
				tmpLP = new LinePart();
				tmpLP.start = tmpLine[0];
				tmpLP.end = tmpLine[1];
				tmpLP.startIdx = i;
				tmpLP.endIdx = i;
				lpList.add( tmpLP );
				continue;
			}else  // 同一个区间
			{
				tmpLP.end = tmpLine[1];
				tmpLP.endIdx = i;
			}
			
		}
		
		//3.逐段求和
		int count = 0;
		for( int i = 0; i < lpList.size(); i ++ )
		{
			LinePart lpPart = lpList.get( i );
			List<int[]> tmpList = new ArrayList<int[]>();
			for( int j = lpPart.startIdx; j <= lpPart.endIdx; j ++ )	//  j <= lpPart.endIdx,包含在内
			{
				int[] tmpLine = lines.get( j );
				int[] copy = new int[2];
				copy[0] = tmpLine[0];
				copy[1] = tmpLine[1];
				tmpList.add( copy );
			}
			int partCount = getPartMinCount( tmpLP.start, tmpLP.end, tmpList );
			count += partCount;
		}
		System.out.println( count );

	}
	
	private static int getPartMinCount( int start, int end, List<int[]> list)
	{
		if( start >= end )
		{
			return 0;
		}
		int minCount = list.size();
		for( int i = 0; i < list.size(); i ++ )
		{
			int tmpCount = 0;
			int[] cur = list.get( i );
			
			// 当它已经无法覆盖
			if( cur[0] > start )
			{
				break;
			}
			
			// 如果end小于start,那这样的线段根本不需要
			if( cur[1] < start  )
			{
				continue;
			}
			
			list.remove( i );
			
			tmpCount ++;
			tmpCount += getPartMinCount( cur[1], end, list);
			
			list.add( i, cur );
			if( tmpCount < minCount )
			{
				minCount = tmpCount;
			}
		}
		
		return minCount;
	}
}

JavaScript code

const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;
void async function() {
    while (line = await readline()) {
        var count = parseInt(line);

        var lines = new Array();
        for (var i = 0; i < count; i++) {
            line = await readline();
            var strStartEnd = line.split(",");
            var startEnd = [];
            startEnd[0] = parseInt(strStartEnd[0]);
            startEnd[1] = parseInt(strStartEnd[1]);
            lines[i] = startEnd;
        }

        processMinLineCount(lines);
    }
}();

function compareLineFun(a, b) {
    for (var i = 0; i < a.length; i++) {
        if (a[i] != b[i]) {
            return a[i] - b[i];
        }
    }
    return 0;
}

function processMinLineCount(lines) {
    // 1. 排序
    lines.sort(compareLineFun);

    // 2. 分段

    // LinePart 的数据结构
    // LinePart  {
    //      start; // 起始点,包含在内
    //      end; // 终止点,包含在内
    //      startIdx; // 对应lines中的起始下标,包含在内
    //      endIdx; // 对应lines中的终止下标,包含在内
    //  }

    var lpList = new Array();
    var tmpLP = {};
    for (var i = 0; i < lines.length; i++) {
        var tmpLine = lines[i];
        if (i == 0) {
            tmpLP.start = tmpLine[0];
            tmpLP.end = tmpLine[1];
            tmpLP.startIdx = 0;
            tmpLP.endIdx = 0;
            lpList.push(tmpLP);
            continue;
        }

        // 不是同一个区间
        if (tmpLine[0] > tmpLP.end) {
            tmpLP = new LinePart();
            tmpLP.start = tmpLine[0];
            tmpLP.end = tmpLine[1];
            tmpLP.startIdx = i;
            tmpLP.endIdx = i;
            lpList.push(tmpLP);
            continue;
        } else // 同一个区间
        {
            tmpLP.end = tmpLine[1];
            tmpLP.endIdx = i;
        }

    }

    //3.逐段求和
    var count = 0;
    for (var i = 0; i < lpList.length; i++) {
        var lpPart = lpList[i];
        var tmpList = new Array();
        for (var j = lpPart.startIdx; j <= lpPart.endIdx; j++) //  j <= lpPart.endIdx,包含在内
        {
            var tmpLine = lines[j];
            var copy = [];
            copy[0] = tmpLine[0];
            copy[1] = tmpLine[1];
            tmpList.push(copy);
        }
        var partCount = getPartMinCount(tmpLP.start, tmpLP.end, tmpList);
        count += partCount;
    }
    console.log(count);
}

function getPartMinCount(start, end, list) {
    if (start >= end) {
        return 0;
    }
    var minCount = list.length;
    for (var i = 0; i < list.length; i++) {
        var tmpCount = 0;
        var cur = list[i];

        // 当它已经无法覆盖
        if (cur[0] > start) {
            break;
        }

        // 如果end小于start,那这样的线段根本不需要
        if (cur[1] < start) {
            continue;
        }

        list.splice(i, 1);

        tmpCount++;
        tmpCount += getPartMinCount(cur[1], end, list);

        list.splice(i, 0, cur);
        if (tmpCount < minCount) {
            minCount = tmpCount;
        }
    }

    return minCount;
}

This question is a bit difficult. From thinking about the algorithm to implementing the code and passing the test, there is a lot of work. Among the Huawei OD exam questions, it is a difficult question.

(over)

Guess you like

Origin blog.csdn.net/ZiJinShi/article/details/133134874