Codeforces Round #513 by Barcelona Bootcamp (rated, Div. 1 + Div. 2) C 【暴力枚举+dp】

版权声明:如需转载,记得标识出处 https://blog.csdn.net/godleaf/article/details/82940317

题目链接:http://codeforces.com/contest/1060/problem/C

思路:

一些暴力看起来会超时的题目还是有必要尝试一下的 ;

题目的意思可以转换成:求两个数组的区间和的最大长度和,并且两个区间和的乘积小于等于x

给出的数组只有2000个,求出所有区间和的时间复杂度大概是4*10^6,求两个数组的区间和就只是多一个常数; 假设一个数组的某个区间和是 n,只需要在另一个数组的区间和中二分找 x/n 的值,得到一个下标 p,那么0到p内在数值上是符合题意的,接下来要解决的就是在0到p中找区间长度最长的,才能得到题目要求的最大值;如果这一步仍然用暴力枚举去找就会超时,这里可以通过一个简单的dp处理一下,因为0和p的的区间和是肯定符合题意的,我们只需要用数组记录前i个区间和中最大区间长度是多少就行了,这样就不用一个个遍历,只需要直接取值就行了;

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

const int Maxn = 2e3+10;

struct Node {
	int cnt, sum;  // cnt 区间长度,sum 区间和 
	bool operator < (const Node &a1) const {
		return sum < a1.sum;
	}
} N[Maxn*Maxn], M[Maxn*Maxn];

int sN[Maxn], sM[Maxn], num[Maxn*Maxn];

int main (void)
{
	int n, m, x, tmp;
	scanf ("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf ("%d", &tmp);
		sN[i] = sN[i-1]+tmp;
	}
	for (int i = 1; i <= m; ++i) {
		scanf ("%d", &tmp);
		sM[i] = sM[i-1]+tmp;
	}
	scanf("%d", &x);
	int n1, m1;
	n1 = 0;
	for (int i = 1; i <= n; ++i) {
		for (int j = i; j <= n; ++j) {
			if(sN[j]-sN[j-i] > x) continue;
			N[n1].cnt = i; N[n1].sum = sN[j]-sN[j-i];
			n1++;
		}
	}
	m1 = 0;
	for (int i = 1; i <= m; ++i) {
		for (int j = i; j <= m; ++j) {
			if(sM[j]-sM[j-i] > x) continue;
			M[m1].cnt = i; M[m1].sum = sM[j]-sM[j-i];
			m1++;
		}
	}
	int ans = 0, p, index;
	sort (N, N+n1); sort (M, M+m1);
	
	num[0] = M[0].cnt;
	for (int i = 1; i < m1; ++i) {  // num[i] 为M[]的前i个区间长度的最大值 
		num[i] = max (num[i-1], M[i].cnt);
	}
	
	for (int i = 0; i < n1; ++i) {
		index = x/N[i].sum;
		int L = 0, R = m1, mid;
		while (L < R) {
			mid = (L+R)/2;
			if (M[mid].sum > index) R = mid;
			else L = mid+1;
		}
		if (--R < 0) continue;  // 上面的二分求得是大于index的,要-1 
		ans = max (ans, N[i].cnt*num[R]); 
	}
	printf ("%d\n", ans);
	return 0;
 } 

猜你喜欢

转载自blog.csdn.net/godleaf/article/details/82940317