真的是贪心吗?

1055.股票买卖II

给定一个长度为 NN 的数组,数组中的第 ii 个数字表示一个给定股票在第 ii 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

输入格式

第一行包含整数 NN,表示数组长度。

第二行包含 NN 个不大于 1000010000 的正整数,表示完整的数组。

输出格式

输出一个整数,表示最大利润。

数据范围

1≤N≤1051≤N≤105

输入样例1:
6
7 1 5 3 6 4
输出样例1:
7
输入样例2:
5
1 2 3 4 5
输出样例2:
4
输入样例3:
5
7 6 4 3 1
输出样例3:
0
样例解释

样例1:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。共得利润 4+3 = 7。

样例2:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

样例3:在这种情况下, 不进行任何交易, 所以最大利润为 0。

贪心:只要第二天比第一题高,就买入卖出。

import java.util.Scanner;

public class Main {
	static Scanner sc = new Scanner(System.in);
	public static void main(String[] args) {
		int n = sc.nextInt();
		int[] a = new int[n];
		for(int i = 0; i < n ; i++)a[i] = sc.nextInt();
		
		int ans = 0;
		for(int i = 1; i < n; i++) {
			if(a[i] > a[i - 1])ans += a[i] - a[i - 1];
		}
		System.out.println(ans);
	}
}

104.货仓选址

在一条数轴上有 NN 家商店,它们的坐标分别为 A1A1~ANAN。

现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送一车商品。

为了提高效率,求把货仓建在何处,可以使得货仓到每家商店的距离之和最小。

输入格式

第一行输入整数N。

第二行N个整数A1A1~ANAN。

输出格式

输出一个整数,表示距离之和的最小值。

数据范围

1≤N≤1000001≤N≤100000

输入样例:
4
6 2 9 1
输出样例:
12

贪心:如果是偶数1,2,6,9。则仓库选在2~6之间都可以。

如果是奇数1,2,6,8,9.则仓库选取6。故仓库为n/2;

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

const int N = 1e5 + 10;
int a[N];
int main(){
    int n;
    scanf("%d",&n);
    for(int i = 0; i < n; i++)cin >> a[i];
    sort(a,a + n);
    int res = a[n >> 1];
    long long ans = 0;
    for(int i = 0; i < n; i++)ans += abs(a[i] - res);
    cout << ans;
    return 0;
}

122.糖果传递

有n个小朋友坐成一圈,每人有a[i]个糖果。

每人只能给左右两人传递糖果。

每人每次传递一个糖果代价为1。

求使所有人获得均等糖果的最小代价。

输入格式

第一行输入一个正整数n,表示小朋友的个数。

接下来n行,每行一个整数a[i],表示第i个小朋友初始得到的糖果的颗数。

输出格式

输出一个整数,表示最小代价。

数据范围

1≤n≤10000001≤n≤1000000
数据保证一定有解。

输入样例:
4
1
2
5
4
输出样例:
4

题解地址:https://www.acwing.com/solution/AcWing/content/955/

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args) {
        int n = sc.nextInt();
        long  sum = 0;
        long[] a = new long[n+1], c = new long[n+1];
        for(int i = 1; i <= n; i++){a[i] = sc.nextInt(); sum += a[i];}

        int avg = (int)(sum / n);

        for(int i = 1; i <= n; i++) c[i] += c[i - 1]  - avg + a[i];

        quick_sort(1,n,c);
        long mid = c[(n+1) / 2];
        long res = 0;
        for(int i = 1; i <= n; i++)res += Math.abs(c[i] - mid);
        System.out.println(res);

    }

    public static void quick_sort(int l, int r, long[] q){
        if(l >= r) return ;
        int i = l - 1, j = r + 1;
        long temp = q[i + r >> 1];
        do{
            do i++; while(q[i] < temp);
            do j--; while (q[j] > temp);
            if(i < j){
                long t = q[i];
                q[i] = q[j];
                q[j] = t;
            }
        }while(i < j);
        quick_sort(l,j,q);quick_sort(j + 1,r,q);
    }
}

112.雷达设备

假设海岸是一条无限长的直线,陆地位于海岸的一侧,海洋位于另外一侧。

每个小岛都位于海洋一侧的某个点上。

雷达装置均位于海岸线上,且雷达的监测范围为d,当小岛与某雷达的距离不超过d时,该小岛可以被雷达覆盖。

我们使用笛卡尔坐标系,定义海岸线为x轴,海的一侧在x轴上方,陆地一侧在x轴下方。

现在给出每个小岛的具体坐标以及雷达的检测范围,请你求出能够使所有小岛都被雷达覆盖所需的最小雷达数目。

输入格式

第一行输入两个整数n和d,分别代表小岛数目和雷达检测范围。

接下来n行,每行输入两个整数,分别代表小岛的x,y轴坐标。

同一行数据之间用空格隔开。

输出格式

输出一个整数,代表所需的最小雷达数目,若没有解决方案则所需数目输出“-1”。

数据范围

1≤n≤10001≤n≤1000

输入样例:
3 2
1 2
-3 1
2 1
输出样例:
2

圆的问题 —> 线段的问题;

每一个海岸在正上方,要包裹此海岸在海岸线上必有一段距离都可以放雷达(高度小于雷达辐射半径时)。

要使雷达的数量最少,当此线段可以重合时,必定选在重合的位置。

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    static Scanner sc = new Scanner(System.in);
    static seg[] segs = new seg[1005];
    public static void main(String[] args) {
        int n = sc.nextInt() ,d = sc.nextInt();
        boolean flag = false;
        for(int i = 0; i < n; i++){
            int x = sc.nextInt(), y = sc.nextInt();
            // 当 高度大于 半径d, 肯定无法辐射到,失败!
            if(y > d)flag = true;
            // 根据 勾股定理 算出线段的距离
            double len = Math.sqrt(d * d - y * y);
            segs[i] = new seg(x-len,x+len);
        }
        if(flag){
            System.out.println("-1");
        }else{
            Arrays.sort(segs,0,n);
            double last = -1e20;
            int res = 0;
            
            for(int i = 0; i < n; i++){
                // 此线段不在 上一个区间内, 多设雷达
                if(last < segs[i].l) {
                    res++;
                    last = segs[i].r;
                }
            }
            System.out.println(res);
        }

    }
    static class seg implements Comparable<seg>{
        double l,r;
        seg(double l, double r){
            this.l = l;
            this.r = r;
        }
        // 将线段从小到大 排序按照 右端点
        @Override
        public int compareTo(seg seg) {
            if(this.r > seg.r)return 1;
            else if(this.r == seg.r)return 0;
            else return -1;
        }
    }
}

1235.付账问题

几个人一起出去吃饭是常有的事。

但在结帐的时候,常常会出现一些争执。

现在有 nn 个人出去吃饭,他们总共消费了 SS 元。

其中第 ii 个人带了 aiai 元。

幸运的是,所有人带的钱的总数是足够付账的,但现在问题来了:每个人分别要出多少钱呢?

为了公平起见,我们希望在总付钱量恰好为 SS 的前提下,最后每个人付的钱的标准差最小。

这里我们约定,每个人支付的钱数可以是任意非负实数,即可以不是 11 分钱的整数倍。

你需要输出最小的标准差是多少。

标准差的介绍:标准差是多个数与它们平均数差值的平方平均数,一般用于刻画这些数之间的“偏差有多大”。

形式化地说,设第 ii 个人付的钱为 bibi 元,那么标准差为 :

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fCMYDAm1-1588155724916)(https://cdn.acwing.com/media/article/image/2019/12/04/19_6734517a16-p1.png)]

输入格式

第一行包含两个整数 n、Sn、S;

第二行包含 nn 个非负整数 a1, …, ana1, …, an。

输出格式

输出最小的标准差,四舍五入保留 4 位小数。

数据范围

1≤n≤5×1051≤n≤5×105,
0≤ai,S≤1090≤ai,S≤109

输入样例1:
5 2333
666 666 666 666 666
输出样例1:
0.0000
输入样例2:
10 30
2 1 4 7 4 8 3 6 4 7
输出样例2:
0.7928

要是方差(标准差的平方)最小,就是使每个人出的钱,尽可能平均,如果都出一样的钱,则标准差为0;

故,当一个人不够出平均数时,就出所有的钱,还差平均数多少,让剩下的所有人平坦;

double avg = s / n, res = 0.0; // avg 是平均数
for(int i = 0; i < n; i++){
 double cur = s / (n - i); // 剩下的人要出的平均的钱
 if(a[i] < cur)cur = a[i]; // 如果出不起该 平均数,则出全部
 res += (cur - avg) * (cur - avg); // 计算方差
 s -= cur; // 剩下的人要出的总金额
}
import java.util.Arrays;
import java.util.Scanner;

public class Main {
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args) {
        int n = sc.nextInt();
        double s = sc.nextDouble();
        int[] a = new int[n];
        for(int i = 0; i < n; i++)a[i] = sc.nextInt();
        Arrays.sort(a);

        double avg = s / n, res = 0.0; // avg 是平均数
        for(int i = 0; i < n; i++){
            double cur = s / (n - i); // 剩下的人要出的平均的钱
            if(a[i] < cur)cur = a[i]; // 如果出不起该 平均数,则出全部
            res += (cur - avg) * (cur - avg); // 计算方差
            s -= cur; // 剩下的人要出的总金额
        }

        System.out.printf("%.4f\n",Math.sqrt(res / n));
    }
}

1239. 乘积最大

给定 NN 个整数 A1,A2,…ANA1,A2,…AN。

请你从中选出 KK 个数,使其乘积最大。

请你求出最大的乘积,由于乘积可能超出整型范围,你只需输出乘积除以 10000000091000000009 的余数。

注意,如果 X<0X<0, 我们定义 XX 除以 10000000091000000009 的余数是负(−X−X)除以 10000000091000000009 的余数,即:0−((0−x)%1000000009)0−((0−x)%1000000009)

输入格式

第一行包含两个整数 NN 和 KK。

以下 NN 行每行一个整数 AiAi。

输出格式

输出一个整数,表示答案。

数据范围

1≤K≤N≤1051≤K≤N≤105,
−105≤Ai≤105−105≤Ai≤105

输入样例1:
5 3
-100000
-10000
2
100000
10000
输出样例1:
999100009
输入样例2:
5 3
-100000
-100000
-2
-100000
-100000
输出样例2:
-999999829

如果K == N 那么全部乘上

如果是K为偶数,那我们永远可以得到整数。每次选取负数最小的两个数与正数最大的两个数乘积进行比较。两两一取;

如果K为奇数,先去最大的数,此时K变成偶数剩下的同偶数做法。

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    static final int MOD = 1000000009;
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args) {
        int n = sc.nextInt(), k = sc.nextInt();
        int[] a = new int[n];
        for(int i = 0; i < n; i ++)a[i] = sc.nextInt();
        Arrays.sort(a);

        int i = 0, j = n - 1, sign = 1;
        int res = 1;
        if((k & 1) == 1){
            res = a[j--];
            if(res < 0)sign = -1;
            k--;
        }
        while(k > 0){
            long x = (long)a[i] * a[i + 1];
            long y = (long)a[j] * a[j - 1];
            if(x * sign > y * sign){
                res = (int) (x % MOD * res % MOD);
                i += 2;
            }else {
                res = (int) (y % MOD * res % MOD);
                j -= 2;
            }
            k -= 2;
        }
        System.out.println(res);
    }
}

1247.后缀表达式

给定 NN 个加号、MM 个减号以及 N+M+1N+M+1 个整数 A1,A2,⋅⋅⋅,AN+M+1A1,A2,···,AN+M+1,小明想知道在所有由这 NN 个加号、MM 个减号以及 N+M+1N+M+1 个整数凑出的合法的后缀表达式中,结果最大的是哪一个?

请你输出这个最大的结果。

例如使用 123+−123+−,则 “23+1−”“23+1−” 这个后缀表达式结果是 44,是最大的。

输入格式

第一行包含两个整数 NN 和 MM。

第二行包含 N+M+1N+M+1 个整数 A1,A2,⋅⋅⋅,AN+M+1A1,A2,···,AN+M+1。

输出格式

输出一个整数,代表答案。

数据范围

0≤N,M≤1050≤N,M≤105,
−109≤Ai≤109−109≤Ai≤109

输入样例:
1 1
1 2 3
输出样例:
4

逆波兰表达式,相当于可以加括号

故当M >1 时 负号个数可以选择 1 ~ M , 例如b - (a-c-d-e-f) 把负号放里面就是正号,放外面则是符号,故1 ~ M 。

若N > 1 同理把 + 号放括号里面 变负号 所以 负号选择范围为1 ~ M + N

第一个数一定为正号,至少选择一个负号,所以初始值设为MAX - MIN

import java.util.Scanner;

public class Main {
    static Scanner sc = new Scanner(System.in);
    static final int inf = 0x3f3f3f3f;
    public static void main(String[] args) {
        int n = sc.nextInt(), m = sc.nextInt();
        Integer[] a = new Integer[n + m + 1];
        int min = inf,max = -inf,f1 = 0, f2 = 0;
        for(int i = 0; i < n + m + 1; i ++){
            a[i] = sc.nextInt();
            if(min > a[i]){
                min = a[i];
                f1 = i;
            }
            if(max < a[i]){
                max = a[i];
                f2 = i;
            }
        }
        long res = 0;
        if(m > 0){
            res = res + max - min;
            for(int i = 0 ; i < n + m + 1; i++)
                if(i != f1 && i != f2)
                    res += Math.abs(a[i]);
        }else{
            for(int i = 0 ; i < n + m + 1; i++)res += a[i];
        }
        System.out.println(res);
    }
}

1248.灵能传输

在游戏《星际争霸 II》中,高阶圣堂武士作为星灵的重要 AOE 单位,在游戏的中后期发挥着重要的作用,其技能”灵能风暴“可以消耗大量的灵能对一片区域内的敌军造成毁灭性的伤害。

经常用于对抗人类的生化部队和虫族的刺蛇飞龙等低血量单位。

你控制着 nn 名高阶圣堂武士,方便起见标为 1,2,⋅⋅⋅,n1,2,···,n。

每名高阶圣堂武士需要一定的灵能来战斗,每个人有一个灵能值 aiai 表示其拥有的灵能的多少(aiai 非负表示这名高阶圣堂武士比在最佳状态下多余了 aiai 点灵能,aiai 为负则表示这名高阶圣堂武士还需要 −ai−ai 点灵能才能到达最佳战斗状态)。

现在系统赋予了你的高阶圣堂武士一个能力,传递灵能,每次你可以选择一个 i∈[2,n−1]i∈[2,n−1],若 ai≥0ai≥0 则其两旁的高阶圣堂武士,也就是 i−1、i+1i−1、i+1 这两名高阶圣堂武士会从 ii 这名高阶圣堂武士这里各抽取 aiai 点灵能;若 ai<0ai<0 则其两旁的高阶圣堂武士,也就是 i−1,i+1i−1,i+1 这两名高阶圣堂武士会给 ii 这名高阶圣堂武士 −ai−ai 点灵能。

形式化来讲就是 ai−1+=ai,ai+1+=ai,ai−=2aiai−1+=ai,ai+1+=ai,ai−=2ai。

灵能是非常高效的作战工具,同时也非常危险且不稳定,一位高阶圣堂武士拥有的灵能过多或者过少都不好,定义一组高阶圣堂武士的不稳定度为 maxni=1|ai|maxi=1n|ai|,请你通过不限次数的传递灵能操作使得你控制的这一组高阶圣堂武士的不稳定度最小。

输入格式

本题包含多组询问。输入的第一行包含一个正整数 TT 表示询问组数。

接下来依次输入每一组询问。

每组询问的第一行包含一个正整数 nn,表示高阶圣堂武士的数量。

接下来一行包含 nn 个数 a1,a2,⋅⋅⋅,ana1,a2,···,an。

输出格式

输出 TT 行。

每行一个整数依次表示每组询问的答案。

数据范围

1≤T≤3,3≤n≤300000,|ai|≤1091≤T≤3,3≤n≤300000,|ai|≤109,
每个评测用例的限制如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0iC9tPw8-1588155724919)(https://cdn.acwing.com/media/article/image/2019/12/05/19_ba773c9e17-QQ%E6%88%AA%E5%9B%BE20191205220735.png)]

输入样例1:
3
3
5 -2 3
4
0 0 0 0
3
1 2 3
输出样例1:
3
0
3
输入样例2:
3
4
-1 -2 -3 7
4
2 3 4 -8
5
-1 -1 6 -1 -1
输出样例2:
5
7
4
样例解释

样例一
对于第一组询问:
对 22 号高阶圣堂武士进行传输操作后 a1=3,a2=2,a3=1a1=3,a2=2,a3=1。答案为 33。
对于第二组询问:
这一组高阶圣堂武士拥有的灵能都正好可以让他们达到最佳战斗状态。

题意就是经过灵能转换后,使最大值最小。

我们发现一次灵能转换为 a[i-1] += a[i], a[i] -= 2a[i], a[i+1] += a[i];

也就是说,a[i] 给两侧的人一人一个a[I],所以每次灵能转换,总灵能是不会变的。故应想到前缀和。

一次灵能转换为s[i-1]+a[i],s[i]-a[i],a[i] = s[i] - s[i-1]; 所以每次交换等于s[i] 与 s[i-1]位置进行交换。

答案就是使s[i]之间的差值最小(差值就是a[i]),就是利用排序。又因为1位置和n位置不能交换,故有初始位置s0,sn。

从s0到最小值,sn到最大值,要是其分布均匀。故将s0最小值,最小值s0这一段,贪成隔一个数的差值。

如果直接排序,肯定导致s[0]~最小值或者s[n]到最大值的差值过大,要使其差值最小

for(int i = (int)s0; i >= 0; i -= 2){
    f[l++] = s[i];st[i] = true;
}
for(int i = (int)sn; i <= n; i += 2){
    f[r--] = s[i];st[i] = true;
}
  • 经过灵能传输,发现只是交换前缀和的位置
  • 转化为前缀和的思想,也就是找最小的差值
  • 又可以无线交换位置(零能传输),又寻找最小差值故用排序
  • s0,sn固定,不能灵能交换,所以中间有段重复的,并且恰好重复一次,所以隔一个选择一个就是最小的差值。
import java.util.Arrays;
import java.util.Scanner;

public class Main {
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args) {
        int T = sc.nextInt();
        while(T-- > 0){
            int n = sc.nextInt();
            int[] a = new int[n+1];
            long[] s = new long[n + 1];
            for(int i = 1; i <= n; i ++){
                a[i] = sc.nextInt();s[i] = s[i - 1] + a[i];
            }
            long s0 = s[0], sn = s[n];
            if(s0 > sn){ // 选定s0比sn更小
                long t = s0;
                s0 = sn;
                sn = t;
            }

            Arrays.sort(s);

            for(int i = 0; i <= n; i++)
                if(s[i] == s0){
                    s0 = i;
                    break;
                }
            for(int i = n; i >= 0; --i)
                if(s[i] == sn){
                    sn = i;
                    break;
                }

            int l = 0, r = n;
            long[] f = new long[n+1];
            boolean[] st = new boolean[n + 1];
            for(int i = (int)s0; i >= 0; i -= 2){
                f[l++] = s[i];st[i] = true;
            }
            for(int i = (int)sn; i <= n; i += 2){
                f[r--] = s[i];st[i] = true;
            }

            for(int i = 0; i <= n; i++)
                if(st[i] == false)f[l++] = s[i];
            long res = 0;
            for(int i = 1; i <= n; i++){
                res= Math.max(res,Math.abs(f[i]-f[i-1]));
            }

            System.out.println(res);
        }
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值