时间限制: 1 Sec 内存限制:128 MB
思路:要求 F(i, j) = ai + aj + | i - j |,可以想到分别从左向右扩张:即若 ak - ai > k - i 则k相对i为更优解 ,和从右向左扩张:即若 ak-aj > j - k 则k相对j为更优解.
设k1为从左到右的最优解,k2为从右到左的最优解,有k1<=k2.
证明:反证法
假若k1>k2,则 有a(k2) > a(k1)
ak1 - ak2 < k1 - k2,
则从右到左不能从k1 扩张到 k2,矛盾.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <iostream> // C++头文件,C++完全兼容C #include <algorithm> // C++头文件,C++完全兼容C #define fre freopen("in.txt","r",stdin) //以文件代替控制台输入,比赛时很常用,能缩短输入测试样例的时间 #define INF 0x3f3f3f3f #define inf 1e60 using namespace std; // C++头文件,C++完全兼容C #define N 100005 // 宏定义 #define LL long long / /宏定义 int num[N]; int v1[N],v2[N]; //记录分别从左和右可以进行扩张的数的坐标 int main() { //fre; //以文件代替控制台输入,比赛时很常用,能缩短输入测试样例的时间 int t; scanf("%d",&t); while(t--){ int n; scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&num[i]); int s = 0; //左边从0开始往右扩张 int p = 1; int cot1 = 0; v1[cot1++]=s; while(p<n-1){ //不能把 第n个记进去 if(num[p]-num[s] > p-s){ v1[cot1++]=p; //满足扩张条件,则计入数组 s=p; } p++ } s = n-1; //右边从n-1开始从左扩张 p = n-2; int cot2 = 0; v2[cot2++]=s; while(p>0){ //不能把第0个记进去 if(num[p]-num[s] > s-p){ v2[cot2++]=p; //满足扩张条件则计入数组 s=p; } p--; } int ans; //两个数组的数最大项可能是同一个,除此之外不会冲突 分类讨论即可 if(v1[cot1-1]==v2[cot2-1]){ //若最大项为同一个 ans = max(num[v1[cot1-1]] + num[v1[cot1-2]] +v1[cot1-1] - v1[cot1-2] , num[v2[cot2-1]] + num[v2[cot2-2]]+ v2[cot2-2] - v2[cot2-1]); }else{ //若最大项不同 ans = num[v1[cot1-1]] + num[v2[cot2-1]] + v2[cot2-1] -v1[cot1-1]; } printf("%d\n",ans); } }