GDUT22级寒假训练专题一

题解

E - 激光炸弹

传送门

题意

    地图上有 n n n个目标,用整数 x i x_i xi, y i y_i yi,表示目标在地图上的位置,每个目标都有一个价值 v i v_i vi。选取一个正方形,边长为 m m m,顶点坐标均为整数,且边平行于 x x x轴, y y y轴。求出正方形能框选到的最大目标价值和。
1 ≤ n ≤ 1 0 4 , 0 ≤ x i , y i ≤ 5 × 1 0 3 , 1 ≤ m ≤ 5 × 1 0 3 , 1 ≤ v i < 100 ,确保输出结果不会超过 32767 1\le n \le10^4,0 \le x_i ,y_i \le 5\times 10^3, 1 \le m \le 5\times 10^3,1 \le v_i < 100,确保输出结果不会超过 32767 1n1040xi,yi5×1031m5×1031vi<100,确保输出结果不会超过32767

思路

    建立一个二维数组 a [ i ] [ j ] a[i][j] a[i][j]储存在坐标 ( i , j ) (i,j) (i,j)上所有目标的价值和。然后求出 a a a的二维前缀和 s u m sum sum,递推公式 : : :
s u m [ i ] [ j ] + = s u m [ i − 1 ] [ j ] + s u m [ i ] [ j − 1 ] − s u m [ i − 1 ] [ j − 1 ] + a [ i ] sum[i][j]+=sum[i−1][j]+sum[i][j−1]−sum[i−1][j−1] + a[i] sum[i][j]+=sum[i1][j]+sum[i][j1]sum[i1][j1]+a[i]
然后枚举每一个点,计算 : : :
m a x ( a n s , s u m [ i ] [ j ] − s u m [ i − m ] [ j ] − s u m [ i ] [ j − m ] + s u m [ i − m ] [ j − m ] ) max(ans,sum[i][j] - sum[i - m][j] - sum[i][j - m] + sum[i - m][j - m]) max(ans,sum[i][j]sum[im][j]sum[i][jm]+sum[im][jm])
枚举完即可得到答案。
    值得注意 x , y x,y x,y最大为5000,最多只能开一个 500 0 2 5000^2 50002大小的int二维数组,需要把 a a a数组和 s u m sum sum数组合并,且 x , y x,y x,y可以取 0 0 0,防止数组越界,输入时 x , y x,y x,y均加一处理。

代码

#include <bits/stdc++.h>
using namespace std;

int main()
{
	int n, m;
	cin >> n >> m;
	int sum[5005][5005] = { 0 };//没有目标地点初始化价值为0
	for (int i = 1; i <= n; i++)
	{
		int x, y, v;
		cin >> x >> y >> v;
		sum[x + 1][y + 1] = v;//防止越界
	}
	for (int i = 1; i <= 5001; i++)
		for (int j = 1; j <= 5001; j++)
			sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + sum[i][j];
	int ans = 0;
	for (int i = m; i <= 5001; i++)
		for (int j = m; j <= 5001; j++)
			ans = max(ans, sum[i][j] - sum[i - m][j] - sum[i][j - m] + sum[i - m][j - m]);
	cout << ans << endl;
	return 0;
}

G - Exams

传送门

题意

    一个人有 m m m门科目需要考试,每一门科目需要 a i a_i ai的复习时间(复习时间可以不用连续),并且有一份 n n n天的考试安排表,其中 d i d_i di表示第 i i i天能考第 i i i门科目,假如 d i d_i di 0 0 0,就代表这一天没有任何科目的考试。试求这个人最少在第几天顺利通过所有考试?(注:这个人一天要么只能考试,要么就只能复习,有考试不可能通过输出 − 1 -1 1
1   ≤   n ,   m   ≤   1 0 5 , 0   ≤   d i   ≤   m , 1   ≤   a i   ≤   1 0 5 1 ≤ n, m ≤ 10^5,0 ≤ d_i ≤ m,1 ≤ ai ≤ 10^5 1 n,m 1050 dim1 ai 105

思路

    首先可以发现对于某一门课目来说,越晚考试,留给我们的复习时间也就越充分,如果这样也有某一门课目过不去,就不能可过去,输出-1即可。
    通过上述思考可以发现,我们可以判断在第 k k k天,我们能不能通过所有考试。按照这个想法,我们只需要枚举每一天,找到能通过所有考试的天数中的第一天即可得到答案。
   但是显然每天都判断一次,时间复杂度为 O ( n 2 ) O(n^2) O(n2),会超时,每次判断都能确定最终答案在其左侧或右侧,因此可以使用二分答案的方法,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

代码

#include <bits/stdc++.h>
using namespace std;

int n, m;
int arr[100005];
int Ti[100005];
int check(int sign)
{
	int bucket[100005] = { 0 };
	int solvearr[100005];
	int countm = 0;
	for (int i = sign; i >= 1; i--)
	{
		if (arr[i] != 0 && bucket[arr[i]] == 0)
		{
			bucket[arr[i]]++;
			countm++;
			solvearr[i] = arr[i];
		}
		else
		{
			solvearr[i] = 0;
		}
	}
	if (countm != m)
		return 0;
	int count0 = 0;
	for (int i = 1; i <= sign; i++)
	{
		if (solvearr[i] == 0)
			count0++;
		else
			count0 -= Ti[arr[i]];
		if (count0 < 0)
			return 0;
	}
	return 1;
}


int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		cin >> arr[i];
	for (int i = 1; i <= m; i++)
		cin >> Ti[i];
	int R = n, L = 1;
	if (!check(n))
	{
		cout << -1 << endl;
		return 0;
	}
	while (R != L)
	{
		int mid = (R + L) / 2;
		if (check(mid))
			R = mid;
		else
			L = mid + 1;
	}
	cout << R << endl;
	return 0;
}

J - 最大连续和

传送门

题意

    给你一个长度为 n n n的整数序列 A 1 , A 2 , ⋯   , A n A_1,A_2,\cdots ,A_n A1,A2,,An ,要求从中找出一段连续的长度不超过 m m m 的非空子序列,使得这个序列的和最大。
1 ≤ n , m ≤ 2 × 1 0 5 1≤n,m≤2×10^5 1n,m2×105

思路

    求一段连续子序列的和,可以建立前缀和数组 s u m sum sum求。考虑到需要找出长度不超过m的和最大的非空子序列,可以用单调队列维护 ( i − m ) (i - m) (im) i − 1 i - 1 i1 m m m个位置中的 s u m [ i ] sum[i] sum[i],
1、维护队首(就是如果队首储存的sum[a]和当前的sum[i]比较a < i - m,那队首就可以被删了)
2、在队尾插入(每插入一个就要从队尾开始往前去判断,如果队尾元素大于 s u m [ i ] sum[i] sum[i],那队尾就可以删除)
3、对于 ( i − m + 1 ) (i - m + 1) (im+1) i i i m m m个元素,最大序列和为 a n s i = s u m [ i ] − 当前队首元素 ans_i = sum[i] -当前队首元素 ansi=sum[i]当前队首元素,只需过一遍序列的前缀和求出 m a x ( a n s 0 , a n s 1 . . . , a n s n ) max(ans_0,ans_1...,ans_n) max(ans0,ans1...,ansn)即为最终答案。时间复杂度 O ( n ) O(n) O(n)

代码

#include <bits/stdc++.h>
using namespace std;

int main()
{
	int n, m;
	int sum[200005] = { 0 };
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		int a;
		cin >> a;
		sum[i] = sum[i - 1] + a;
	}
	deque<int> line;
	int ans = sum[1];
	for (int i = 0; i <= n; i++)
	{
		while (!line.empty() && i - line.front() > m)//维护队首
			line.pop_front();
		if(!line.empty())
			ans = max(ans, sum[i] - sum[line.front()]);
		while (!line.empty() && sum[i] <= sum[line.back()])//维护队尾
			line.pop_back();
		line.push_back(i);
	}
	cout << ans << endl;
	return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值