首页 > 试题广场 >

商品交易

[编程题]商品交易
  • 热度指数:2886 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解

珐达采下个月要去鸥洲各国考察一趟,采购流通神秘石并从中搞点油水。

珐达采会按顺序依次经过序号分别为1, 2, 3, …, n的鸥洲国家,在第i个国家神秘石的流通价格为Ai鸥。因为行程紧张,在每个国家的停留时间有限,所以他只能花费Ai鸥买入一块神秘石,或者卖出一块手中的神秘石获得Ai鸥,或者什么都不做,而且因为神秘石的保存需要极其先进的高级材料容器,其材料稀有且制作困难,珐达采只有一份容器,故无论何时珐达采手里 最多只能拥有一块神秘石。

珐达采想知道最终能从中获利最大多少鸥。因为交易需要手续费,所以珐达采还想知道在获利最大收益的同时,最少需要交易多少次。因为珐达采是大财阀,所以你可以认为他一开始金钱无限。


输入描述:
第一行一个数n。(1≤n≤100000)

第二行n个数,第i个数表示Ai。(1≤Ai≤1e9)


输出描述:
共一行,两个数,分别代表最大收益和对应的最少交易次数。
示例1

输入

5
9 7 10 1 5

输出

7 4
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Scanner;

/*
 * @author Moze 2020
 * 思想是动态规划,因为直接穷举会超时。
 * 动态规划的递推式是下面这样的:
 * ①初始index=1时,只有两种:买(M[1][1]=-A[1]),或者不买M[1][0]=0
 * 同时可以得到交易次数:买(op[1][1]=1),或者不买M[1][0]=0;
 * ②接下来就是2~n-1次:设为index次
 *   又分为有还是没有,时间有限,只介绍没有,有逻辑基本类似,可以见代码
 *   有的话,要么是index-1次有,index没卖,或者index-1时没有有,但是index买了。
 *   对这两个值进行比较,然后再根据谁大,来获得op的值即可
 * ③n次时同②,只不过由于最后卖了肯定比不卖挣得多,因此,拿了出来只计算卖了的
 * 然后就直接得到结果即可
 */
public class Main {
	static int n;
	static long A[];
	//每种情况赚了多少钱:第一维度为第几个城市,第二维度为是否含有
	static long M[][];
	//每种情况至少需要多少交易次数
	static long op[][];
	public static void main(String[] args) {
		Scanner aa = new Scanner(System.in);
		n=aa.nextInt();
		A= new long[n+1];
		for(int i=1;i<=n;i++)
			A[i]=aa.nextInt();
		M = new long[n+1][2];
		op = new long[n+1][2];
		M[1][0]=0;op[1][0]=0;
		M[1][1]=-A[1];op[1][1]=1;
		for(int i=2;i<n;i++){
			//i时没有
			if(M[i-1][1]+A[i] > M[i-1][0]){
				M[i][0]=M[i-1][1]+A[i];
				op[i][0]=op[i-1][1]+1;
			}else if(M[i-1][1]+A[i] == M[i-1][0]){
				M[i][0]=M[i-1][1]+A[i];
				op[i][0]=Math.min(op[i-1][1]+1,op[i-1][0]);
			}else{
				M[i][0] =  M[i-1][0];
				op[i][0] = op[i-1][0];
			}
			//i时有
			if(M[i-1][1]>M[i-1][0]-A[i]){
				M[i][1]=M[i-1][1];
				op[i][1]=op[i-1][1];
			}else if(M[i-1][1]==M[i-1][0]-A[i]){
				M[i][1]=M[i-1][1];
				op[i][1]=Math.min(op[i-1][1], op[i-1][0]+1);
			}else{
				M[i][1]=M[i-1][0]-A[i];
				op[i][1]= op[i-1][0]+1;
			}
		}
		long max=0;
		long min_op;
		if(M[n-1][0]>M[n-1][1]+A[n]){
			max=M[n-1][0];
			min_op=op[n-1][0];
		}else if(M[n-1][0]==M[n-1][1]+A[n]){
			max=M[n-1][0];
			min_op = Math.min(op[n-1][0], op[n-1][1]+1);
		}else{
			max=M[n-1][1]+A[n];
			min_op = op[n-1][1]+1;
		}
		if(max<=0){
			System.out.println(0+" "+0);
			return ;
		}
		System.out.println(max+" "+min_op);
	}
	
	
}



发表于 2020-06-28 21:47:55 回复(0)
贪心算法 和力扣无限次买卖股票一个道理,应该就是拿那个改的,只要下一天是涨的就买,第二天卖了,这样只能算出收益,麻烦的是算最少交易次数,设置标志位为c,如果c是0的话,可以加一,但是后期把c改为1,因为在连续增长的区间其实在一开始买就行了就行了,如果遇到价格降低的区间,那么意味着在一开始应该抛售,因此b+c,也就是b+1,同时把c值为零。
注意在最后一定要再次计算b+c,因为最后两个数有可能是上升也可能是下降,因此需要再次计算
注意两点
1.度小满的题都是大数,趁早数组弄长,数据格式不要写int
2.交易的理解,这道题买和卖都是交易,是两次,不是一买一卖算一次
#include<stdio.h>
int main(){
    long int a=0,b=0,c=0;
    int n;
    long int d[100000];
    scanf("%ld",&n);
    for(int i=0;i<n;i++){
        scanf("%ld",&d[i]);
        
    }
    for(int i=1;i<n;i++){
        if(d[i]>d[i-1]){
            a=a+d[i]-d[i-1];
            if(c==0){
                b++;
            }
            c=1;
        }
        
         if(d[i]<d[i-1]){
            b=b+c;
            
            c=0;
        }

    }
    b=b+c;
    printf("%ld %ld",a,b);
    return 0;
    
    
    
    
}


编辑于 2020-06-25 10:15:25 回复(0)
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n,cnt=0;
    cin>>n;
    long a[n],s=0;
    bool d = false;
    for(int i=0;i<n;i++){
        cin>>a[i];
    }
    for(int i=1;i<n;i++){
        if(a[i]>a[i-1]){
            s += a[i]-a[i-1];
            if(!d)
                cnt++;
            d = true;
        }
        if(a[i]<a[i-1]){
            cnt += d;
            d = false;
        }
    }
    cnt += d;
    cout<<s<<" "<<cnt<<endl;
    return 0;
}

发表于 2019-10-21 17:56:16 回复(1)
import java.util.*;
import java.lang.Math;
public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] array = new int[n];
        for(int i = 0; i < n; i++){
            array[i] = sc.nextInt();
        }
        long[][] dp = new long[n][2];
        dp[0][0] = 0;
        dp[0][1] = -array[0];
        int n0 = 0;
        int n1 = 1;
        for (int i = 1; i < n; i++){
            if (dp[i-1][0] < array[i] + dp[i-1][1]) {
                n0 = n1 + 1;
            }
            dp[i][0] = Math.max(dp[i-1][0], array[i] + dp[i-1][1]);
            
            if (dp[i-1][1] < dp[i-1][0] - array[i]) {
                n1 = n0 + 1;
            }
            dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - array[i]);
        }
        long res = Math.max(dp[n-1][0], dp[n-1][1]);
        if (res == dp[n-1][0]) {
            System.out.println(res + " " + n0);
        } else if (res == dp[n-1][1]) {
            System.out.println(res + " " + n1);
        }
        
    }
}

发表于 2020-06-21 11:44:30 回复(0)
//动态规划思路,
//列状态:1、第i个国家;2、手中有无神秘石,
//即dp[i][1]为经过第i个国家后,手中有神秘石,dp[i][0]为经过第i个国家后,手中无神秘石。
//状态转移:dp[i][0] = max(dp[i - 1][0], price[i] + dp[i - 1][1]);表示经过第i个国家后的值等于经过第i-1个国家时手中没神秘石,经过第i个国家后还是没有神秘石;和有神秘石但在第i个国家中卖了。取两种情况的最大值即可。
//dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - price[i]);的情况同理。
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int num;
cin >> num;
long long* price = new long long[num];
for (int i = 0; i < num; i++)
{
cin >> price[i];
}
long long** dp = new long long* [num];
for (int i = 0; i < num; i++)
dp[i] = new long long[2];
dp[0][0] = 0, dp[0][1] = -price[0];
int n0 = 0, n1 = 1;
for (int i = 1; i < num; i++)
{
if (dp[i - 1][0] < price[i] + dp[i - 1][1])n0 = n1 + 1;
dp[i][0] = max(dp[i - 1][0], price[i] + dp[i - 1][1]);

if (dp[i - 1][1] < dp[i - 1][0] - price[i])n1 =n0 + 1;
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - price[i]);


}
long long win = max(dp[num - 1][0], dp[num - 1][1]);
int ans = win == dp[num - 1][1] ? n1 : n0;
cout << win << " " << ans << endl;
return 0;
}
编辑于 2020-06-13 14:12:37 回复(0)
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,res1=0,res2=0;
    long long num=0;
    cin>>n;
    vector<int>a(n);
    for(int i=0;i<n;i++)
        cin>>a[i];
    for(int i=1;i<n;i++)
    {
        if (a[i] > a[i - 1]) 
        {
            num += a[i] - a[i - 1];
            res1 += res2 ^ 1;
            res2 = 1;
        } 
        else if (a[i] < a[i - 1]) {
            res1 += res2;
            res2 = 0;
        }
    }
    cout<<num<<" "<<res1+res2<<endl;
    return 0;
}

发表于 2019-08-18 22:12:01 回复(0)
n = int(input())
num = list(map(int, input().split()))
deal = 0
profit = 0
cnt = 0
for i in range(1,n):
    if num[i] > num[i-1]:
        profit += num[i]-num[i-1]
        if deal == 0:
            cnt += 1
        deal = 1
    if num[i] < num[i-1]:
        cnt += deal
        deal = 0
print(profit,cnt+deal)

发表于 2019-08-07 09:03:02 回复(3)
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr = new int[n];

        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
        }

        // 不持有宝石的收益和交易次数 
        long value0 = 0,cnt0 = 0;
        // 持有宝石的收益和交易次数 
        long value1 = -arr[0],cnt1 = 1;
        
        for (int i = 1; i < n; i++) {
            if(value0<value1+arr[i]){
                value0 = value1+arr[i];
                cnt0 = cnt1+1;
            }
            if(value1<value0-arr[i]){
                value1 = value0-arr[i];
                cnt1 = cnt0+1;
            }
        }

        long max = 0,minCnt = 0;
        for (int i = 1; i < n; i++) {
            if(value0>max){
                max = value0;
                minCnt = cnt0;
            }else if(value0==max){
                minCnt = Math.min(minCnt,cnt0);
            }
        }
        System.out.println(max+" "+minCnt);
    }
}


发表于 2022-08-31 18:10:53 回复(0)
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        long[] arr = new long[n];
        for (int i = 0; i < n; i++) {
            arr[i] = in.nextLong();
        }
        fun(arr);
    }

    // 最大的收益和交易次数-无脑动态规划
    public static void fun(long[] arr) {
        int n = arr.length;
        long[][] dp = new long[n][2]; // 走到第i个国家的持有或卖出的最大收益 1持有 0卖出
        int[][] time = new int[n][2];
        dp[0][0] = 0; // base case
        dp[0][1] = -arr[0];
        time[0][0] = 0;
        time[0][1] = 1;
        for (int i = 1; i < n; i++) {
            if (dp[i-1][1] + arr[i] > dp[i-1][0]) {
                dp[i][0] = dp[i-1][1] + arr[i]; // 卖出
                time[i][0] = time[i-1][1] + 1;
            } else {
                dp[i][0] = dp[i-1][0];
                time[i][0] = time[i-1][0];
            }
            if (dp[i-1][0] - arr[i] > dp[i-1][1]) {
                dp[i][1] = dp[i-1][0] - arr[i]; // 买入
                time[i][1] = time[i-1][0] + 1;
            } else {
                dp[i][1] = dp[i-1][1];
                time[i][1] = time[i-1][1];
            }

        }
        System.out.println(dp[n-1][0] + " "+ time[n-1][0]);
    }

}
发表于 2022-07-11 17:29:50 回复(0)
不能用int表示最大收益!!!
import java.util.*;
public class Main {

    public static  void main(String []args){
        Scanner in=new Scanner(System.in);
        while(in.hasNext()){

            int n=in.nextInt();
            long nums[]=new long [n];
            for (int i = 0; i < n; i++) {
                nums[i]=in.nextInt();
            }

            int out=0;
            long m=0;
            int i=0;
            int j=-1;
            for (int k = 1; k <n ; k++) {
                if (j==-1){
                    if (nums[k]>nums[i])j=k;
                    if (nums[k]<nums[i])i=k;

                }
                else {
                    if (nums[k]<nums[j]){
                        out++;
                        m+=nums[j]-nums[i];
                        i=k;
                        j=-1;
                    }
                    else if (nums[k]>nums[j])j=k;

                }


            }
            if (j!=-1){
                out++;
                m+=nums[j]-nums[i];
            }
            System.out.println(m+" "+out*2);
        }
    }
}


发表于 2020-07-31 15:17:33 回复(0)
//不用动态规划
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner sc  =new Scanner(System.in);
        int len = sc.nextInt();
        int[] in = new int[len];
        int judge = -1,time = 0;
        long out = 0;
        for(int i=0;i<len;i++){
            in[i] = sc.nextInt();
        }
        sc.close();
        for(int i=0;i<len-1;i++){
            if(judge==-1){
                if(in[i]<in[i+1]){
                    judge = in[i];
                    time++;
                }
            }else{
                if(judge<in[i]&&in[i]>in[i+1]){
                    out+=in[i]-judge;
                    time++;
                    judge = -1;
                }
            }
        }
        if(judge!=-1){
            out+=in[len-1]-judge;
            time++;
        }
        System.out.println(out+" "+time);
    }

}
发表于 2019-08-13 20:58:53 回复(0)
采用动态规划,dp[i][j]代表在第i个国家且容器剩余容量为j时的收益。由于容器最大容量为1,所以j只能为0或1。
由于除了收益还要比较交易次数,dp中不仅存储了收益还存储了已交易次数
dp[i][0] = max(dp[i-1][1] + a[i], dp[i-1][0])
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - a[i])
(次数的计算比较简单所以不写了)

import java.util.*;
public class Main {
    public static class State {
        long money;
        int deal;
        public State(long money, int deal) {
            this.money = money;
            this.deal = deal;
        }
        public State(State h) {
            this.money = h.money;
            this.deal = h.deal;
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNextInt()) {
            int n = sc.nextInt();
            int[] a = new int[n];
            for (int i = 0; i < n; ++i) {
                a[i] = sc.nextInt();
            }
            State[][] dp = new State[n][2];
            dp[0][0] = new State(0, 0);
            dp[0][1] = new State(-a[0], 1);
            for (int i = 1; i < n; ++i) {
                State sell = new State(dp[i - 1][1]);
                sell.money += a[i];
                sell.deal += 1;
                dp[i][0] = max(sell, dp[i - 1][0]);

                State buy = new State(dp[i - 1][0]);
                buy.money -= a[i];
                buy.deal += 1;
                dp[i][1] = max(dp[i - 1][1], buy);
            }
            System.out.println(dp[n - 1][0].money + " " + dp[n - 1][0].deal);
        }
    }
    public static State max(State h1, State h2) {
        if (h1.money != h2.money) {
            return h1.money > h2.money ? h1 : h2;
        }
        else {
            return h1.deal <= h2.deal ? h1 : h2;
        }
    }
}




编辑于 2019-08-11 14:50:18 回复(2)