acwing314. 低买

给定一段时间内股票的每日售价(正 16 位整数)。

你可以选择在任何一天购买股票。

每次你选择购买时,当前的股票价格必须严格低于你之前购买股票时的价格。

编写一个程序,确定你应该在哪些天购进股票,可以使得你能够购买股票的次数最大化。

例如,下面是一个股票价格时间表:

Day 1 2 3 4 5 6 7 8 9 10 11 12

Price 68 69 54 64 68 64 70 67 78 62 98 87
如果每次购买都必须遵循当前股票价格严格低于之前购买股票时的价格,那么投资者最多可以购买四次该股票。

买进方案之一为:

Day 2 5 6 10

Price 69 68 64 62
输入格式
第 1 行包含整数 N,表示给出的股票价格的天数。

第 2 至最后一行,共包含 N 个整数,每行 10 个,最后一行可能不够 10 个,表示 N 天的股票价格。

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

输出格式
输出占一行,包含两个整数,分别表示最大买进股票次数以及可以达到最大买进次数的方案数。

如果两种方案的买入日序列不同,但是价格序列相同,则认为这是相同的方案(只计算一次)。

数据范围
1≤N≤5000,
保证答案均不超过 int 范围。

输入样例1:
12
68 69 54 64 68 64 70 67 78 62
98 87
输出样例1:
4 2
输入样例2:
5
4 3 2 1 1
输出样例2:
4 1

题意:求最大下降子序列,但是要求不重的方案数

题解: 首先看数据范围5000 可以o(n^2) 但是这个方案数的话就不太好考虑
那么看如何求不重的方案数:
(2022暑假牛客多校和杭电多校 出过几道字符串去重dp 也是求不重方案数)
那么方法有以下几种:

  1. 直接从构造开始想: 考虑当前位与其他方案不同(之前位的方案数以及当前位不同)
  2. 去重记录重复方案数 最后删掉(废话)

思考该方程的本质:让每个位置的元素继承大于其自身的前缀中的最优解。

dp版本

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>

#define int long long
using namespace std;
const int N = 5100;
int n, ans, s[N], f[N], a[N];
int res;

signed main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin>>a[i];
        f[i] = 1;
    }
    for (int i = 1; i <= n; i++) {
        //s[i]=1;
        for (int j = 1; j < i; j++) {
            if (a[i] < a[j])f[i] = max(f[i], f[j] + 1);
        }
        for (int j = 1; j < i; j++) {
            if (a[i] == a[j] && f[i] == f[j]) {
                s[j] = 0;                                  //保证每两种种方案的相同一位必有偏差  当前状态下不管前面的状态只需要保证当前这一位的状态不同即可
            } else if (a[i] < a[j] && (f[i] == f[j] + 1)) {
                s[i] += s[j];                              //前缀加方案和
            }
        }    
        if (f[i] == 1)s[i] = 1;
        res=max(res,f[i]);
    }
    for (int i = 1; i <= n; i++) {
        if (f[i] == res)ans += s[i];
    }
    printf("%d %d", res, ans);
}

记忆化搜索版本(其他作者)dfs暴力

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010;
int a[N], f[N], dp[N];

int dfs(int t) {
    //cout << a[t] << ' ';
    int i,j, res = 0;
    if (f[t] == 1) return 1;
    if (dp[t] != -1) return dp[t];//如果已经记录过则直接返回。
    int r[N];
    int cnt = 0;
    for (i = t-1; i >= 1; i--) {//从后往前
        if (a[i] > a[t] && f[t] == f[i] + 1) {
            int pd = 1;
            for (j = 1; j <= cnt; j++) {//判断是否这个数已经出现过
                if (a[i] == r[j]) {
                    pd = 0;
                    break;
                }
            }
            if (pd == 1) {
                r[++cnt] = a[i];
                res += dfs(i);
            }
        }
    }
    return dp[t]=res;
}


int main() {
    int n, i, j;
    cin >> n;
    for (i = 1; i <= n; i++) cin >> a[i];
    for (i = 1; i <= n; i++) {
        f[i] = 1;
        for (j = 1; j < i; j++) {
            if (a[i] < a[j]) f[i] = max(f[i], f[j] + 1);
        }
    }
    int ans = 0;
    for (i = 1; i <= n; i++) ans = max(ans, f[i]);
    memset(dp, -1, sizeof dp);
    int res = 0;
    int r[N],cnt=0;
    for (i = n; i >=1; i--) {
        if (ans == f[i]) {
            int pd = 1;
            for (j = 1; j <= cnt; j++) {
                if (a[i] == r[j]) {
                    pd = 0;`在这里插入代码片`
                    break;
                }
            }
            if (pd == 1) {
                r[++cnt] = a[i];
                res += dfs(i);
            }
        }
    }
    cout << ans << ' ' << res;
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值