Problem D. 宝石 (100)
时间限制 1000 ms 内存限制 256 MB
现有一个宝石清单,其中有nn种不同宝石的单价和数量,第ii种宝石的价值为vivi,数量为wiwi。
清单中宝石的总价值为。
请你从清单中选择某个连续的区间,保持其中各种宝石的数量不变,但对其中宝石的单价进行翻转。请计算如何选择列表中的区间,才能使列表中宝石的总价值最大?
输入数据
输入数据有三行,第一行为一个正整数n(1≤n≤5×10^3)n(1≤n≤5×10^3),表示清单中宝石的种类,
第二行为nn个整数v1v1 ~ vnvn,表示每种宝石的单价(1≤vi≤10^3)(1≤vi≤10^3),
第三行为nn个整数w1w1 ~ wnwn,表示每种宝石的数量(1≤wi≤10^3)(1≤wi≤10^3)。
输出数据
在单独的行中输出结果,即最大的总价值。
样例输入
5
1 2 5 4 3
1 2 3 4 5
样例输出
55
说明
注:翻转是指将区间中第一种和最后一种宝石的单价对调,第二种和倒数第二种宝石的单价对调,依此类推。
思路:
本题翻转的时候可以采用圆和半径的方式,用两层for循环分别枚举半径和圆心进行数值的交换;
本题注意循环过程中的圆心的位置有两种情况:
1.圆心在数上 eg:1 2 3 反转后3 2 1
2.圆心在两个数之间 eg:1 2 3 4 反转后: 4 3 2 1
我们可以看作在遍历的时候,圆心是 1 1.5 2 2.5 ...这样变化的
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
long long v[6000];
long long w[6000];
int main() {
int n;
scanf("%d", &n);
long long sum = 0;
for (int i = 1; i <= n; i++) {
scanf("%lld", &v[i]);
}
for (int i = 1; i <= n; i++) {
scanf("%lld", &w[i]);
sum += v[i] * w[i];
}
long long duo = 0;//计算反转后和翻转前的差
long long ans = 0;//记录最优解
int r;
for (int i = 1; i <= n; i++) {
duo = 0;
if (i % 2 == 1)r = n / 2 + 1; //定义半径
else r = n / 2;
for (int j = 0;j<=r&&i+j<=n&&i-j>=0; j++) {//圆心在数上
duo += v[i+j] * w[i - j]+ v[i - j] * w[i + j]-
(v[i - j] * w[i - j] + v[i + j] * w[i + j]);
ans = max(ans, duo);
}
duo = 0;//圆心在数中间
for (int j = 0; j <= r && i + j <= n && i - j >= 0; j++) {
duo += v[i + j + 1] * w[i - j] + v[i - j] * w[i + j + 1] -
(v[i + j + 1] * w[i + j + 1] + v[i - j] * w[i - j]);
ans = max(ans, duo);
}
}
printf("%lld",ans+sum );
}
反思:
注意半径的取值,这一点在解题的时候发生了错误;
数组一定要开够,在学校的oj平台上一直报TLE,我以为是复杂度太高,最后发现是数组没开够;