[题解] [洛谷P1108] 低价购买

wxy3265·2024-04-25 15:44·12 次阅读

[题解] [洛谷P1108] 低价购买

[洛谷P1108] 低价购买

题目描述#

给定一个序列,求这个序列的最长严格下降子序列的长度和数量,特别的是如果两个子序列只要每个数都相同,无论原来的位置,都算作同一个子序列。

输入格式#

第一行共一个整数 n(1n5000) 表示序列长度。

第二行一行 n 个整数,表示序列的每个元素。保证是大小不超过 216 的正整数。

输出格式#

输出共一行两个整数,分别为最大购买次数和拥有最大购买次数的方案数(数据保证 231 )。

题解#

水蓝题一道。数据范围允许 O(n2) 复杂度做法,故用最基础的求 lis 的dp方法即可解决第一问。

问题的关键是如何统计出得到 lis 的方案数。自然地可以想到在转移状态的同时记录一下得到当前状态的方案数 cnti ,如果找到了更优状态,就把前一个状态的方案数继承过来,如果找到了和当前状态一样优的子状态,就把那个状态的方案数叠加上去即可。

最后剩下的问题就是重复的方案,实际上这中情况仅限于前一个状态对应的最后一位 aj 相等的情况,不难发现对于两个状态相同的 j1,j2(j1<j2) 来说,j1 的方案是包括在 j2 中的,因此我们只统计较为靠后的状态的方案数即可,可以通过倒着循环和打标记的方法解决。最后统计答案的时候用类似的方法将所有不同的状态等于 ans 的不相等的方案数累加即可。

AC代码#

Copy
#include <iostream> #define int long long using namespace std; const int MAXN = 5003; const int MAXA = 1e5 + 3; int n, a[MAXN]; int f[MAXN], cnt[MAXN]; // 分别记录长度和方案数 int vis[MAXA]; // 标记是否出现过相同状态 signed main() { cin >> n; // 输入与初始化 for (int i = 1; i <= n; i++) { cin >> a[i]; cnt[i] = f[i] = 1; } int ans = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j < i; j++) vis[a[j]] = 0; // 重置标记 for (int j = i - 1; j > 0; j--) { if (a[i] < a[j]) { // 合法的状态 if (f[j] + 1 > f[i]) { // 更优的状态转移 f[i] = f[j] + 1; // 继承方案数 vis[a[j]] = f[i]; cnt[i] = cnt[j]; } else if (f[j] + 1 == f[i]) { // 与当前状态一样优的状态 // 累加方案数 if (vis[a[j]] != f[i]) cnt[i] += cnt[j]; // 不是相同的状态就累加方案数 vis[a[j]] = f[i]; } } } ans = max(ans, f[i]); // 更新答案 } int anscnt = 0; // 统计方案数 for (int i = 1; i <= n; i++) vis[a[i]] = false; // 初始化标记 for (int i = n; i > 0; i--) { if (vis[a[i]]) continue; // 如果统计过相同的状态就跳过 if (f[i] == ans) anscnt += cnt[i], vis[a[i]] = true; // 如果是最优状态就累加到答案里 } // 输出答案 cout << ans << ' ' << anscnt << '\n'; return 0; }
posted @   wxy3265  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
目录