2022牛客多校联赛第二场 题解

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;
    }
}

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值