LOADING

加载过慢请开启缓存 浏览器默认开启

记录最优方案数的dp

低价购买

题目描述

“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你上次购买它的价格购买它。买的次数越多越好!你的目标是在遵循以上建议的前提下,求你最多能购买股票的次数。你将被给出一段时间内一支股票每天的出售价,你可以选择在哪些天购买这支股票。每次购买都必须遵循“低价购买;再低价购买”的原则。写一个程序计算最大购买次数。

这里是某支股票的价格清单:

$$
\def\arraystretch{1.5}
\begin{array}{|c|c|c|c|c|c|c|c|c|c|c|c|c|}\hline
\textsf{日期} & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 \cr\hline
\textsf{价格} & 68 & 69 & 54 & 64 & 68 & 64 & 70 & 67 & 78 & 62& 98 & 87 \cr\hline
\end{array}
$$
最优秀的投资者可以购买最多 $4$ 次股票,可行方案中的一种是:
$$
\def\arraystretch{1.5}
\begin{array}{|c|c|c|c|c|}\hline
\textsf{日期} & 2 & 5 & 6 & 10 \cr\hline
\textsf{价格} & 69 & 68 & 64 & 62 \cr\hline
\end{array}
$$

输入格式

第一行共一个整数 $N\ (1 \le N \le 5000)$,股票发行天数

第二行一行 $N$ 个整数,是每天的股票价格。保证是大小不超过 $2^{16}$ 的正整数。

输出格式

输出共一行两个整数,分别为最大购买次数和拥有最大购买次数的方案数(数据保证 $ \le 2^{31}$)当二种方案“看起来一样”时(就是说它们构成的价格队列一样的时候),这 $2$ 种方案被认为是相同的。

样例输入 #1

12
68 69 54 64 68 64 70 67 78 62 98 87

样例输出 #1

4 2

思路

f[i]表示以数字a[i]结尾且长度为dp[i]的方案数,那么当a[i]!=a[j]时,出现dp[i]=dp[j]+1的情况,直接在**f[i]加上f[j],但当a[j]=a[i]时,因为我们对f[i]的定义可得,f[i]f[j]**此时是有重合的,也就是说只保留一个最多方案的f[i]即可

最后遍历一遍数组,加上所有等于答案长度的方案数即可

#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int N=1e5+10;
const int mod=1e9+7;
int dp[N],a[N];
int f[N];
void solve()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
          cin>>a[i];
          dp[i]=f[i]=1;
    }
    int ans1=1;
    for(int i = 2; i <= n; i++) {
          for(int j = 1; j < i; j++){
               if(a[i] < a[j]) {
                    if(dp[i]<dp[j]+1){
                         dp[i] = max(dp[i], dp[j] + 1);
                         f[i]=f[j];
                    }
                    else if(dp[i]==dp[j]+1) f[i]+=f[j];
               }
               if(a[i]==a[j]) dp[j]=f[j]=0;
          }
          ans1 = max(ans1, dp[i]);
     }
     int num=0;
     for(int i=1;i<=n;i++){
          if(dp[i]==ans1) num+=f[i];
     }
     cout<<ans1<<' '<<num<<endl;
}
signed main()
{
    int t=1;
    //cin>>t;
    while(t--)
    {
        solve();
    }
}

同样的记录背包最优方案数在acwing有一道板题

image-20240913105957884

这里用g[i]表示背包体积为i时的最优方案数

如果选第i个物品能做到让价值增加,那么舍弃掉原先的方案数,用g[j-v]的值来覆盖掉g[j];

如果选与不选对结果没有影响,即价值一样时,需要把两种状态的方案汇总到一起,g[j]+=g[j-v];

最后的g[V]即是最优情况下的方案总和

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
const int mod=1e9+7;
int dp[N];
int a[N],b[N];
int n,V;
vector<int>e[N];
int g[N];

void solve()
{
   int n,V;
   cin>>n>>V;
   for(int i=0;i<=V;i++) g[i]=1;
   for(int i=1;i<=n;i++)
   {
        int v,w;
        cin>>v>>w;
        for(int j=V;j>=v;j--)
        {
            if(dp[j]<dp[j-v]+w)
            {
                dp[j]=dp[j-v]+w;
                g[j]=g[j-v];
            }
            else if(dp[j]==dp[j-v]+w)
            {
                g[j]+=g[j-v];
                g[j]%=mod;
            }
        }
   }
   cout<<g[V]<<endl;
}
signed main()
{
    int t=1;
    //cin>>t;
    while(t--)
    {
        solve();
    }
}