G题 Link with Monotonic Subsequence
题目大意
给定一个长度为n,元素为1~n的数列,要求取得一种排列情况使得最长非连续升序和降序子序列两者的较大值取到最小。
思路
将长度为n的序列分为√n段长度为√n的序列,每段倒置,末尾不足√n的一段视为单独一段。
例如长度为9的序列改为3 2 1 6 5 4 9 8 7
长度为11的序列为4 3 2 1 8 7 6 5 11 10 9
操作后得出最小的较大值√n
代码
c++
#include<iostream>
#include<cmath>
using namespace std;
int main() {
int t;
cin >> t; \\t为用例个数
for (int i = 0; i < t; i++) {
int n;
cin >> n;
int g = (int)sqrt(n - 1) + 1; \\g为√n
int temp = g;
int k = 0;
while (k < n) { \\while循环给数列数组赋值,k为数组下标
cout << temp << " ";
if (temp % g == 1) {
if (temp + 2 * g - 1 > n)temp = n;
else temp += 2 * g - 1;
}
else temp--;
k++;
}
cout << endl;
}
}
J题 LInk with Arithmetic Progression
题目大意
给定一个数列,其中每个数都有一个修改机会,使得数列为等差数列,要求修改值的平方和达到最小值。
思路
将数组第i个值ai视为指标坐标系上x=i,y=ai的点,即求一个较为贴合坐标系上所有点的直线,参考数学方法线性回归。
代码
import java.util.Scanner; public class J { public static void main(String[] args) { Scanner input =new Scanner(System.in); int t=input.nextInt(); for(int i=0;i<t;i++){ int n= input.nextInt(); int[] num=new int[n]; for(int j=0;j<n;j++){ num[j]= input.nextInt(); } double ave=GetAve(num); double b=GetB(num,ave); double a=GetA(num,b,ave); double sum=GetSum(num,b,a); System.out.println(sum); } } public static double GetA(int[] num,double b,double ave){ \\计算'a' return ave-b*(num.length+1)/2.0; } public static double GetB(int[] num,double ave){ \\计算'b' double n=0; double m=0; for(int i=1;i<= num.length;i++){ n+=(i-(num.length+1)/2.0)*(num[i-1]-ave); m+=Math.pow(i-(num.length+1)/2.0,2); } return n/m; } public static double GetSum(int[] num,double b,double a){ \\计算平方和 double sum=0; for(int i=1;i<= num.length;i++){ sum+=Math.pow(b*i+a-num[i-1],2); } return sum; } public static double GetAve(int[] num){ \\计算数组平均值 double sum=0; for(int i=0;i<num.length;i++){ sum+=num[i]; } return sum/num.length; } }