1 solutions

  • 0
    @ 2024-8-21 3:59:24

    题面描述

    塔子哥接到一项任务,需要在一条长为 NN 公里的河流上监测水质。河流沿线有 KK 个候选地点适合建设水质监测站,每个监测站的覆盖半径为 RR 公里,建设一个监测站的成本为 MM。任务是计算出最少需要多少经费才能覆盖整条河流,如果无法通过在所有候选地点建设监测站来实现覆盖,则输出 "-1"。输入包括河流长度、覆盖半径、建设成本以及候选地点的具体位置,输出则是最少经费或 "-1"。

    思路

    区间覆盖模板题目,这道题和区间覆盖的区别在于区间固定,我们使用贪心去解决对应的问题。

    对于地址ii,假设地址小于ii的都已经被覆盖,那么如果我们需要覆盖它,我们可以选择的基站位置在范围[iR,i+R][i-R,i+R]内。假设现在存在两个基站xxyy(y>x)(y>x)都满足对应的要求,那么我们更偏向于选择yy基站,因为y>xy>x,这样yy的基站可以比xx基站覆盖更多后面的地址

    因此,我们对于每一个地址ii,我们都要进行for  j=min(N,i+R)  to   iR+1j=1for~~ j = min(N, i+R)~~ to~~~ i-R+1,j-=1的循环(这里枚举到iR+1i-R+1,而不枚举到iRi-R的原因在于一旦选择iRi-R这个基站,那么肯定存在大于jj的部分实数地址无法被覆盖),从min(N,i+R)min(N,i+R)开始找基站,一旦找到就调用对应的基站。调用基站jj后,那么我们范围[jR,j+R][j-R,j+R]的所有地址都会被覆盖,因此,下一次我们需要枚举的地址应该从j+Rj+R开始(不是j+R+1j+R+1,因为我们需要保证所有实数地址被覆盖,而不是所有整数地址被覆盖)。

    代码

    CPP

    #include <bits/stdc++.h>
    using namespace std;
    
    int N, R, M; // 河流长度、监测站覆盖半径、单个监测站建设成本
    int arr[3005]; // 用于记录候选基站位置
    
    int main() {
        cin >> N >> R >> M; // 输入河流长度、覆盖半径和建设成本
        int num;
        while (cin >> num) {
            arr[num] = 1; // 将候选基站位置标记为1
        }
        
        int ans = 0; // 用于记录总费用
        for (int i = 0; i <= N; i++) { // 遍历所有地址
            bool f = false; // 标记当前地址是否找到可用基站
            // 从当前地址向右寻找能够覆盖该地址的基站
            for (int j = min(N, i + R); j >= i - R + 1; j--) { // 逆序查找
                if (arr[j] == 1) { // 找到可用的基站
                    ans += M; // 增加建设成本
                    i = j + R - 1; // 更新下一个需要查找的地址为j+R
                    if (i + 1 == N) { // 特例处理:覆盖到N即可
                        cout << ans << endl; // 输出总费用
                        return 0;
                    }
                    f = true; // 标记找到基站
                    break; // 跳出内层循环
                }
            }
            if (!f) { // 如果当前地址没有找到可用基站
                cout << -1 << endl; // 输出-1表示无法覆盖
                return 0;
            }
        }
        cout << ans << endl; // 输出最终所需的总费用
        return 0;
    }
    
    

    python

    N, R, M = map(int, input().split())
    arr = [0] * 3005
    line = [int(i) for i in input().split()]#输入
    for i in line:
        arr[i] = 1
    ans = 0
    i = 0
    
    while i <= N:#枚举所有地址
        f = False
        for j in range(min(i + R, N), i - R, -1):#找对应的基站
            if arr[j] == 1:
                ans += M
                i = j + R - 1#下一次我们需要枚举的地址为j+R
                if i + 1 == N:#特例,因为我们不需要覆盖大于N的部分实数地址,所以只要覆盖到N即可
                    print(ans)
                    exit(0)
                f = True#一旦找到就记录,并且推出循环。
                break
        if not f:#没找到输出-1
            print(-1)
            exit(0)
        i += 1
    print(ans)
    
    

    Java

    import java.util.Scanner;
    
    public class Main {
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            int N = scanner.nextInt();
            int R = scanner.nextInt();
            int M = scanner.nextInt();//输入
            int[] arr = new int[3005];
    
            for (int i = 0; i < 3005; i++) {
                arr[i] = 0;
            }
    		int num;
            while(scanner.hasNext()){
                num = scanner.nextInt();
                arr[num] = 1;
            }
    
            int ans = 0;
            int i = 0;
    
            while (i <= N) {//枚举所有地址
                boolean f = false;
    
                for (int j = Math.min(i + R, N); j >= i - R + 1; j--) {//找对应的基站
                    if (arr[j] == 1) {
                        ans += M;
                        i = j + R - 1;//下一次我们需要枚举的地址为j+R
    
                        if (i + 1 == N) {//特例,因为我们不需要覆盖大于N的部分实数地址,所以只要覆盖到N即可
                            System.out.println(ans);
                            System.exit(0);
                        }
    
                        f = true;//一旦找到就记录,并且退出循环。
                        break;
                    }
                }
    
                if (!f) {//没找到输出-1
                    System.out.println(-1);
                    System.exit(0);
                }
    
                i++;
            }
    
            System.out.println(ans);
        }
    }
    
    

    Go

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	var N, R, M int
    	fmt.Scan(&N, &R, &M)
    	arr := make([]int, 3005)//输入
    
    	var num int
    	for {
    		_, err := fmt.Scan(&num)
    		if err != nil {
    			break
    		}
    		arr[num] = 1
    	}
    
    	ans := 0
    	i := 0
    
    	for i <= N {//枚举所有地址
    		f := false
    
    		for j := min(N, i+R); j >= i - R + 1; j-- {//找对应的基站
    			if arr[j] == 1 {
    				ans += M
    				i = j + R - 1//下一次我们需要枚举的地址为j+R
    
    				if i+1 == N {//特例,因为我们不需要覆盖大于N的部分实数地址,所以只要覆盖到N即可
    					fmt.Println(ans)
    					return
    				}
    
    				f = true//一旦找到就记录,并且退出循环。
    				break
    			}
    		}
    
    		if !f {//没找到输出-1
    			fmt.Println(-1)
    			return
    		}
    
    		i++
    	}
    
    	fmt.Println(ans)
    }
    
    func min(a, b int) int {
    	if a < b {
    		return a
    	}
    	return b
    }
    
    

    Js

    const readline = require('readline');
    
    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });
    
    let N, R, M;
    let arr = new Array(3005).fill(0);
    
    rl.question('', (line1) => {
        const input1 = line1.trim().split(' ').map(Number);
        N = input1[0];
        R = input1[1];
        M = input1[2];
    
        rl.question('', (line2) => {
            const input2 = line2.trim().split(' ').map(Number);
            for (let i = 0; i < input2.length; i++) {
                arr[input2[i]] = 1;
            }//输入
    
            let ans = 0;
            let i = 0;
    
            while (i <= N) {//枚举所有地址
                let f = false;
    
                for (let j = Math.min(i + R, N); j >= i - R + 1; j--) {//找对应的基站
                    if (arr[j] === 1) {
                        ans += M;
                        i = j + R - 1;//下一次我们需要枚举的地址为j+R
    
                        if (i + 1 === N) {//特例,因为我们不需要覆盖大于N的部分实数地址,所以只要覆盖到N即可
                            console.log(ans);
                            process.exit(0);
                        }
    
                        f = true;//一旦找到就记录,并且退出循环。
                        break;
                    }
                }
    
                if (!f) {//没找到输出-1
                    console.log(-1);
                    process.exit(0);
                }
    
                i++;
            }
    
            console.log(ans);
            process.exit(0);
        });
    });
    
    
    • 1

    2023.05.17-暑期实习-华为-第二题-河流水质监测

    Information

    ID
    29
    Time
    1000ms
    Memory
    256MiB
    Difficulty
    5
    Tags
    # Submissions
    80
    Accepted
    15
    Uploaded By