首页 > 试题广场 >

水平线-研发

[编程题]水平线-研发
  • 热度指数:4334 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解

伞屉国是一个以太阳能为主要发电手段的国家,因此他们国家中有着非常多的太阳能基站,链接着的基站会组合成一个发电集群。但是不幸的是伞屉国不时会遭遇滔天的洪水,当洪水淹没基站时,基站只能停止发电,同时被迫断开与相邻基站的链接。你作为伞屉国的洪水观察员,有着这样的任务:在洪水到来时,计算出发电集群被洪水淹没后被拆分成了多少个集群。

由于远古的宇宙战争的原因,伞屉文明是一个二维世界里的文明,所以你可以这样理解发电基站的位置与他们的链接关系:给你一个一维数组a,长度为n,表示了n个基站的位置高度信息。数组的第i个元素a[i]表示第i个基站的海拔高度是a[i],而下标相邻的基站才相邻并且建立链接,即x号基站与x-1号基站、x+1号基站相邻。特别的,1号基站仅与2号相邻,而n号基站仅与n-1号基站相邻。当一场海拔高度为y的洪水到来时,海拔高度小于等于y的基站都会被认为需要停止发电,同时断开与相邻基站的链接。


输入描述:
每个输入数据包含一个测试点。

第一行为一个正整数n,表示发电基站的个数 (0 < n <= 200000)

接下来一行有n个空格隔开的数字,表示n个基站的海拔高度,第i个数字a[i]即为第i个基站的海拔高度,对于任意的i(1<=i<=n),有(0 <= a[i] < 2^31-1)

接下来一行有一个正整数q(0 < q <= 200000),表示接下来有q场洪水

接下来一行有q个整数,第j个整数y[j]表示第j场洪水的海拔为y[j],对于任意的j(1<=j<=n),有(-2^31 < y[j] < 2^31-1)


输出描述:
输出q行,每行一个整数,第j行的整数ans表示在第j场洪水中,发电基站会被分割成ans个集群。标准答案保证最后一个整数后也有换行。
示例1

输入

10
6 12 20 14 15 15 7 19 18 13 
6
15 23 19 1 17 24

输出

2
0
1
1
2
0
这道题数据范围很大。显然是本次最难的题。
每次洪水来的时候会淹没掉一些发电机。 如果我们能得到哪些发电机沉下去了, 就可以枚举每一个发电机的编号来进行处理。
 而每个沉下去的发电机有三种情况。
1. 两侧的发电机都沉下去了, 集群数 - 1;
2. 只有一侧的发电机沉下去, 集群数不变;
3. 两侧的发电机都未沉下去, 集群数 + 1.

但是如果在线做的话, 显然会TLE.

如果洪水是逐渐上涨, 那么每个发电机只会被淹没掉一次, 沉下去的发电机不可能再浮起来。 则我们对洪水高度和发电机都排序, 每次不同的洪水高度, 看成是洪水上涨又新淹没了一堆发电机, 将其记录在答案数组中, 最后一并输出即可。 离线算法的典型运用。
注意边界条件。 我们可以假设0号和n+1号发电机一直在水下即可。

时间复杂度O(nlogn + mlogm)  刚好可以过这个题。

#include<bits/stdc++.h>
using namespace std;
 
struct nums{
    int idx;
    int h;
 
    bool operator <(nums other){
        return h < other.h;
    }
};
 
const int maxn = 200006;
nums a[maxn], b[maxn];
int ans[maxn];
int sunk[maxn];
 
int n, m;
 
int main(){
    scanf("%d", &n);
 
    for(int i = 1; i <= n; i++){
        a[i].idx = i;
        scanf("%d", &a[i].h);
    }
 
    sort(a + 1, a + n + 1);
    scanf("%d", &m);
 
    for(int i = 1; i <= m; i++){
        b[i].idx = i;
        scanf("%d", &b[i].h);
    }
 
    sort(b + 1, b + m + 1);
 
    int last = 1, ret = 1;
 
    sunk[0] = sunk[n+1] = 1;
 
    for(int i = 1; i <= m; i++){
        while(last <= n && a[last].h <= b[i].h){
 
            int idx = a[last].idx;
 
            sunk[idx] = 1;
            int adj = sunk[idx - 1] + sunk[idx + 1];
            if(adj == 0)ret++;
            else if(adj == 2)ret--;
            last++;
        }
 
        ans[b[i].idx] = ret;
    }
 
    for(int i = 1; i <= m; i++){
        printf("%d\n", ans[i]);
    }
 
    return 0;
}


发表于 2020-01-05 16:13:36 回复(6)
#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

void Flood_Fighting() {
	int n = 0;//n个发电基站
	int q = 0;//q场洪水
	cin >> n;
	vector<pair<int,int>> power_station_height(n + 2);//存放每个发电基站的高度,n+2是为了防止下标越界,键值对,first用来保存发电站高度,second用来保存发电站位置

	for (int i = 1; i <= n; i++) {
		cin >> power_station_height[i].first;
		power_station_height[i].second = i;
	}

	cin >> q;
	vector<pair<int,int>> flood_height(q);//存放每场洪水的高度

	for (int i = 0; i < q; i++) {
		cin >> flood_height[i].first;
		flood_height[i].second = i;
	}

	vector<pair<int, int>> tmp(power_station_height);//临时数组,用于存放已排序的各发电站高度

	sort(tmp.begin() + 1, tmp.end() - 1);//对发电站高度排序
	sort(flood_height.begin(), flood_height.end());//对洪水高度排序

	vector<int> result(q, 0);//记录每次洪水过后的ans群数结果

	int r = 1;//用来记录上一次洪水淹没的最后一个发电站在tmp中的位置
	int count = 1;//因为洪水而分成的ans群数
	vector<int> sk(n + 2, 0);//状态数组,状态为0代表没有被淹,1代表被淹
	sk[0] = sk[n + 1] = 1;
	for (int i = 0; i < q; i++) {
		for ( int k = r ; k <= n; k++) {
			if (tmp[k].first <= flood_height[i].first) {
				sk[tmp[k].second] = 1;

				//每个沉下去的发电机有三种情况。
				//1. 两侧的发电机都沉下去了, 集群数 - 1;
				//2. 只有一侧的发电机沉下去, 集群数不变;
				//3. 两侧的发电机都未沉下去, 集群数 + 1.
				if (sk[tmp[k].second + 1] == 1 && sk[tmp[k].second - 1] == 1) {
					count--;
				}
				else if (sk[tmp[k].second + 1] != 1 && sk[tmp[k].second - 1] != 1) {
					count++;
				}

			}
			else {
				result[flood_height[i].second] = count;
				r = k;
				break;
			}
		}
	}

	for (auto e : result) {
		cout << e << endl;
	}
}

int main() {
	Flood_Fighting();
	return 0;
}

发表于 2020-10-13 18:07:45 回复(0)
enb = int(input())
a_alti = [int(i) for i in input().split(" ")]
hx_t = int(input())
hx_h = [int(i) for i in input().split(" ")]
rsp_a = list(range(0, enb + 2))
rsp_a[0] = -1
rsp_a[enb + 1] = -1
hx_new_index = [index for index, value in sorted(enumerate(hx_h), key=lambda h: h[1])]
a_new_index = [index for index, value in sorted(enumerate(a_alti), key=lambda h: h[1])]
cluster_num = [0] * hx_t
k = 1
for i in range(hx_t):
    new_cluster = 0
    while k <= enb and a_alti[a_new_index[k-1]] <= hx_h[hx_new_index[i]]:
        if rsp_a[a_new_index[k-1]] == -1 and rsp_a[a_new_index[k-1]+2] == -1:
            new_cluster = new_cluster - 1
        elif rsp_a[a_new_index[k-1]] != -1 and rsp_a[a_new_index[k-1]+2] != -1:
            new_cluster = new_cluster + 1
        rsp_a[a_new_index[k-1]+1] = -1
        k = k + 1
    if i == 0:
        cluster_num[hx_new_index[i]] = 1 + new_cluster
    else:
        cluster_num[hx_new_index[i]] = cluster_num[hx_new_index[i - 1]] + new_cluster
for cn in cluster_num:
    print(cn)

等到两个数组后分别排序,基本逻辑就是海啸从低到高,电站海拔也从低到高,被低的海啸淹没的电站也一定被高的海啸淹没,
初始集群为1,没当一个电站淹没时,若左右均已淹没则集群数减一,左右均未被淹没时集群数加一,有一边被淹没时集群数不变

发表于 2020-03-07 10:35:43 回复(3)
用了haozheyan97的思路,通过了。C#版:
using System;

namespace NetEase
{
    class Program
    {
        static void Main(string[] args)
        {
            int n = int.Parse(Console.ReadLine());            
            string[] _a = Console.ReadLine().Split(' ');
            Gen[] a = new Gen[n];
            Gen[] a2 = new Gen[n];
            for (int i = 0; i < a.Length; i++)
            {
                a[i].height = a2[i].height = int.Parse(_a[i]);
                a[i].index = i;
                
            }
            Array.Sort(a);

            int q = int.Parse(Console.ReadLine());
            string[] _j = Console.ReadLine().Split(' ');
            Gen[] j = new Gen[q];
            for (int i = 0; i < j.Length; i++)
            {
                j[i].height = int.Parse(_j[i]);
                j[i].index = i;
            }
            Array.Sort(j);

            int[] results = new int[q];

            int index = 0;
            int result = 1;
            for (int i = 0; i < q; i++)
            {
                while (index < n && a[index] <= j[i])
                {
                    a2[a[index].index].sunk = true;
                    if ((a[index].index == 0 || a2[a[index].index - 1].sunk) && (a[index].index == n - 1 || a2[a[index].index + 1].sunk))
                    {
                        result--;
                    }
                    else if ((a[index].index != 0 && !a2[a[index].index - 1].sunk) && (a[index].index != n - 1 && !a2[a[index].index + 1].sunk))
                    {
                        result++;
                    }
                    index++;
                }
                results[j[i].index] = result;
            }

            foreach(int i in results)
            {
                Console.WriteLine(i);
            }
            Console.WriteLine();
        }
    }

    struct Gen : IComparable
    {
        public int height;
        public int index;
        public bool sunk;

        public int CompareTo(object obj)
        {
            return height.CompareTo(((Gen)obj).height);
        }
        public static bool operator <=(Gen a, Gen b)
        {
            return a.height <= b.height;
        }

        public static bool operator >=(Gen a, Gen b)
        {
            return a.height >= b.height;
        }
    }
}

发表于 2020-09-09 00:54:45 回复(0)

Java不超时版本
加了快速输入输出才能过,要么T

import java.io.*;
import java.util.Scanner;


import java.lang.reflect.Array;
import java.util.*;

/**
 * @Author: Shang Huaipeng
 * @Date: 2020/4/1 14:36
 * @Version 1.0
 */
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        try {
            StreamTokenizer sc = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
            sc.nextToken();
            int n = (int)sc.nval;
            int lo[][] = new int[n][2];
            for (int i = 0; i < n; i++) {
                sc.nextToken();
                lo[i][0] = (int)sc.nval;
                lo[i][1] = i + 1;
            }
            sc.nextToken();
            int m = (int)sc.nval;

            int yan[][] = new int[m][2];
            for (int i = 0; i < m; i++) {
                sc.nextToken();
                yan[i][0] = (int)sc.nval;
                yan[i][1] = i;
            }

            Arrays.sort(lo, (o1, o2) -> o1[0] - o2[0]);
            Arrays.sort(yan, (o1, o2) -> o1[0] - o2[0]);
            int ans[] = new int[m];
            boolean vis[] = new boolean[n + 2];
            int kuai = 1;
            int j = 0;
            vis[0] = vis[n + 1] = true;
            PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

            for (int i = 0; i < m; i++) {
                for (; j < n && lo[j][0] <= yan[i][0]; j++) {
                    vis[lo[j][1]] = true;
                    int tot = 0;
                    if (vis[lo[j][1] - 1]) tot++;
                    if (vis[lo[j][1] + 1]) tot++;
                    if (tot == 2) kuai--;
                    if (tot == 0) kuai++;

                }
                ans[yan[i][1]] = kuai;
            }
            for (int i = 0; i < m; i++) {
                out.println(ans[i]);
            }
            out.flush();
        }
        catch (Exception e){

        }
    }

}
发表于 2020-04-10 20:07:42 回复(3)
复杂度太高, 超时怎么办?
import re
n = int(input())
a = [int(num) for num in input().split()]
q = int(input())
y = [int(num) for num in input().split()]
for i in y:
    a1 = [*map(lambda x: '1' if x<=i else '0', a)]
    print(len(re.findall('0+', ''.join(a1))))


发表于 2020-01-03 10:24:57 回复(0)
尝试讲了半天思路,发现越来越乱,算了简单说说吧:
离线算法,求出“每个基站恰好被淹没的情况下,连通块的数量”
先将基站按高度排序,可以想象一下洪水褪去的过程,基站依次显露出来。每冒出一基站(记其位置为idx)来,求一下当前的连通块数(不要从头数,很傻的,计算露头前后的块数的变化量就好,比如idx-1和idx+1都已经露头,说明新露头的把它俩连起来了,那块数就在上一个基站的基础上减一);
然后就简单了,每询问一个洪水高度,直接去有序的基站数组里二分就行了。
有几个要点,要把基站的高度和位置绑定到一起排序,需要手写cmp或重载运算符;另外要考虑数个基站同时露头的情况,所以

#include<iostream>
#include<algorithm>
#include<string>
using namespace std;

typedef struct Node{
    int h, idx, cnt;
} node;

int cmp(node n1, node n2){
    return n1.h > n2.h;
}

int main(){
    int n, q, tmp;
    node NodeList[200010];
    bool joined[200010] {false};
    cin>>n;
    for (int i=0;i<n;i++){
        cin>>tmp;
        node newnode;
        newnode.idx = i;
        newnode.h = tmp;
        newnode.cnt = 0;
        NodeList[i] = newnode;
    }
    sort(NodeList, NodeList+n, cmp);
    
    int cnt = 0;
    int i=0,j=0;
    while (i<n){
        j = i;
        while (j<n && NodeList[j].h==NodeList[i].h) j++;
        j--;
        for (int k=i;k<=j;k++) NodeList[k].cnt = cnt;
        for (int k=i;k<=j;k++){
            int idx = NodeList[k].idx;
            if (idx==0) cnt += 1-joined[1];
            else if (idx==n-1) cnt += 1-joined[n-2];
            else cnt += 1-joined[idx-1]-joined[idx+1];
            joined[idx] = true;
        }
        i = j+1;
    }
    
    //for (int i=0;i<n;i++){
    //    cout<<NodeList[i].idx<<" "<<NodeList[i].h<<" "<<NodeList[i].cnt<<endl;
   // }
    
    cin>>q;
    for (int i=0;i<q;i++){
        cin>>tmp;
        if (tmp < NodeList[n-1].h){
            cout<<1<<endl;
            continue;
        }
        int l=0,r=n-1;    //NodeList从大到小,找处第一个小于等于tmp的值
        while (l<r){
            int k=(l+r)>>1;
            if (NodeList[k].h > tmp) l = k+1;
            else r = k;
        }
        cout<<NodeList[l].cnt<<endl;
    }
    return 0;    
}


发表于 2022-08-25 22:53:01 回复(0)
补充一下顶楼的在线做法
#include <iostream>
#include <map>

using namespace std;


map<int,int> mp;
int jz[200010];
int n,m;

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d",&jz[i]);
    }
    jz[0]=-100;
    jz[n+1]=-100;
    mp[-1]=1;
    int ptr=1;
    while(ptr<=n){
        bool l,r;
        l=jz[ptr]>jz[ptr-1];
        while(ptr<n&&jz[ptr]==jz[ptr+1]){
            ptr++;
        }
        r=jz[ptr]>jz[ptr+1];
        if(l&&r){
            mp[jz[ptr]]--;
        }else if(!l&&!r){
            mp[jz[ptr]]++;
        }
        ptr++;
    }
    int sum=0;
    int mx=0;
    for(auto itor=mp.begin();itor!=mp.end();++itor){
        sum+=itor->second;
        itor->second=sum;
        mx=itor->first;
    }
    scanf("%d",&m);
    while(m--){
        int tmp;
        scanf("%d",&tmp);
        if(tmp<0){
            printf("1\n");
            continue;
        }
        if(tmp>=mx){
            printf("0\n");
            continue;
        }
        printf("%d\n",(--mp.upper_bound(tmp))->second);
    }
}

发表于 2022-02-18 01:01:06 回复(0)
每一个查询都可能淹没一些发电站,淹没多少个发电站显然可以用排序+二分查找的方式获得。
提前对发电站高度排序,注意用结构体保留发电站序号。
预处理淹没第i高度发电站(显然1..i-1高度也会被淹没)时集群数量,可递推;
此时有以下几个分支(下面数字表示原始序号),初始状态1个集群。
(1)淹没第1号发电站同时第2号发电站没有被淹没,此时集群数量不变;第n个同理;
(2)淹没第1号发电站同时第2号发电站已经被淹没,此时集群数-1;n同理;
(3)淹没的发电站如果是x,x-1和x+1都已经被淹没,此时x会把x-1和x+1连接起来,集群数-1;
(4)淹没的发电站如果是x,x-1或x+1中的某一个被淹没,此时x会连在其中一个上,集群数不变;
(5)淹没的发电站如果是x,x-1和x+1都没有被淹没,x将序列分成两段,集群数+1;
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
struct node
{
    int id,v;
    bool operator<(const node B)const
    {
        return v<B.v;
    }
}a[200005];
int n,q,vis[200005],ans[200005];
int main()
{
    ios::sync_with_stdio(0),cin.tie(0);
    int i,j;
    cin>>n;
    for(i=1; i<=n; i++)
        cin>>a[i].v,a[i].id=i;
    sort(a+1,a+n+1);
    ans[0]=1;
    for(i=1; i<=n; i++)
    {
        if(a[i].id==1&&vis[2]==1||a[i].id==n&&vis[n-1]==1)
            ans[i]=ans[i-1]-1;
        else if(a[i].id==1||a[i].id==n)
            ans[i]=ans[i-1];
        else if(vis[a[i].id-1]==1&&vis[a[i].id+1]==1)
            ans[i]=ans[i-1]-1;
        else if(vis[a[i].id-1]==1||vis[a[i].id+1]==1)
            ans[i]=ans[i-1];
        else
            ans[i]=ans[i-1]+1;
        vis[a[i].id]=1;
    }
    cin>>q;
    while(q--)
    {
        node temp;
        temp.id=0;
        cin>>temp.v;
        int t=upper_bound(a+1,a+n+1, temp)-a;
        if(t==n+1)
            cout<<0<<endl;
        else
            cout<<ans[t-1]<<endl;
        }
    return 0;
}


发表于 2021-03-16 23:01:17 回复(0)
n1 = int(input())
x = list(input().split())
n2 = int(input())
y = list(input().split())
for i in range(n2):
    l1 = []
    answer = 0
    gaodu = int(y[i])
    for j in range(n1):
        if int(x[j]) > gaodu:
                if j == n1-1:
                    answer += 1
                elif int(x[j+1])<= gaodu:
                    answer +=1
    print(answer)


复杂度超了,咋改
发表于 2020-08-11 00:35:42 回复(0)
为什么超时呀
#include <iostream>
#include<vector>
using namespace std;
int max1(vector<int> &vec) {
    int max = vec[0];
    for (int i = 0; i < vec.size(); i++) {
        if (vec[i] > max) {
            max = vec[i];
        }
    }
    return max;
}
int min1(vector<int> &vec) {
    int min = vec[0];
    for (int i = 0; i < vec.size(); i++) {
        if (vec[i] < min) {
            min = vec[i];
        }

    }
    return min;
}
int main()
{
    int n;
    int m;
    int q = 0;
    int num;
    int sum = 0;

    cout << "请输入n个塔的高度:" << endl;
    cin >> n;


    vector<int> ivec1, ivec2;

    for (int i = 0; i < n; i++) {
        cin >> num;

        ivec1.push_back(num);

    }
    cout << "请输入q场大水:" << endl;

    cin >> q;



    for (int i = 0; i < q; i++) {
        cin >> num;

        ivec2.push_back(num);
    }

    for (int i = 0; i < ivec2.size(); i++) {


        sum = 0;
        int count=0;

        if (ivec1.size() == 1) {//如果长度为1的时候
            if (ivec1[0] > ivec2[i]) {
                sum = 1;
            }
            if (ivec1[0] <= ivec2[i]) {
                sum = 0;
            }
        }
        //如果长度大于1的时候
        if (ivec1.size() > 1) {
            if (ivec2[i] >= max1(ivec1)) {
                sum = 0;
            }
            else if (ivec2[i] < min1(ivec1)) {
                sum = 1;
            }
            else {
                for (int j = 1; j < ivec1.size(); j++) {
                    if (ivec1[j - 1] <= ivec2[i] && ivec1[j] > ivec2[i]) {
                        sum++;
                    }

                }
                if (ivec1[0] > ivec2[i]) {
                    sum++;
                }
                
            }
            
        }
        
        
        cout << sum << endl;


    }


}

发表于 2020-07-14 10:07:07 回复(0)
对问题进行离线处理,由小到大排序,再对节点由小到大扫过去更新答案,时间复杂度再 O(n + m)
发表于 2020-01-03 22:18:41 回复(0)
import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        Scanner in = new Scanner(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        Solver solver = new Solver();
        solver.solve(1, in, out);
        out.close();
    }

    static class Solver {
        public void solve(int testNumber, Scanner in, PrintWriter out) {
            int n = in.nextInt();
            int[] altitudes = new int[n];
            for (int i = 0; i < n; ++i) {
                altitudes[i] = in.nextInt();
            }
            int q = in.nextInt();
            for (int i = 0; i < q; ++i) {
                out.println(countOfClusters(altitudes, in.nextInt()));
            }
        }

        private int countOfClusters(int[] altitudes, int floodAltitude) {
            int count = 0;
            if (altitudes[0] > floodAltitude) {
                count++;
            }
            for (int i = 1; i < altitudes.length; ++i) {
                if (altitudes[i] > floodAltitude && altitudes[i - 1] <= floodAltitude) {
                    count++;
                }
            }
            return count;
        }

    }
}

发表于 2019-12-19 17:12:16 回复(1)