G题 Link with Monotonic Subsequence
题目大意:
给出一个整数n,在[1,n]的全排列中,找出一个排列p,满足max(lis(p),lds(p))的值在全排列中最小,其中lis(p)为排列p的最长递增子序列长度,lds(p)为排列p的最长递减子序列长度。若有多个满足条件的排列p,则输出其中一个。
思路:
将[1,n]n个整数尽量平均分组,即分成ceil(sqrt(n))组,除了最后一组,每组ceil(sqrt(n))个元素,每个组中为连续的递减(或递增)元素,组与组之间又逐渐递增(或递减)。如下图。
代码:
#include <iostream>
#include <cmath>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int n;
scanf("%d", &n);
int num = ceil(sqrt(n)); //组数,同时也是除了最后一个组,每个组的个数
if (num * num == n) { //如果n是完全平方数,即每个组元素个数相同
for (int i = num; i <= n; i += num) {
int temp = i; //当前组的第一个元素
for (int j = 0; j < num; j++) {
cout << temp--; //递减输出
if (!(i == n && j == num - 1)) cout << ' '; //如果不是当前样例最后一个数,则输出空格
}
}
cout << endl;
}
else { //n不是完全平方数时,最后一个组个数不足num
int last = n % num; //最后一个组元素个数
for (int i = num; i <= n - last; i += num) {
int temp = i;
for (int j = 0; j < num; j++) {
cout << temp-- << " ";
}
}
for (int i = 0; i < last; i++) {
cout << n-- << " ";
}
cout << endl;
}
}
return 0;
}
J题 Link with Arithmetic Progression
题目大意:给出一个长度为n的整数数列,要求对这个数列进行修改(数列中每个数最多修改一次),使其变成等差数列,同时修改所耗费的代价和最小。其中将某个数a修改为b,所耗费的代价为(a−b′)2 。
样例:
(这输入样例的第三组数据怎么这么臭啊(恼))
思路:
将数列(数组)下标看作x坐标,数列元素值看作y坐标,最终修改后的等差数列的点则坐落在一条直线上。要使修改幅度尽量小,再结合图,很容易联想到高中时所学的线性回归方程。根据公式求出回归方程的参数a与b,得出拟合曲线y=bx+a,即为代价最小时的等差数列所对应的直线方程。由此求出最小代价。
代码(Java):
(队友用的Java,补题时用的C++不知为何过不了,但思路是一样的)
import java.util.Scanner;
public class Main {
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){
return ave-b*(num.length+1)/2.0;
}
public static double GetB(int[] num,double ave){
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;
}
}