zoukankan      html  css  js  c++  java
  • 股票买卖类线性Dp

    # 标题 通过率 难度
    83 股票的最大利润 55.66% 简单
    332 股票交易 55.29% 简单
    1054 股票买卖 63.55% 简单
    1055 股票买卖 II 75.60% 简单
    1056 股票买卖 III 57.16% 简单
    1057 股票买卖 IV 51.54% 中等
    1058 股票买卖 V 66.37% 中等
    1059 股票买卖 VI 74.71% 中等
    314 低买 57.26% 中等
    1. 纪念品 : https://www.acwing.com/problem/content/description/1165/

    股票的最大利润

    注意本题不买也可以,而且有可能nums.size()<2;

    class Solution {
    public:
        int maxDiff(vector<int>& nums) {
            if(nums.size()<2) return 0;
            int cmin=nums[0],ans=0;
            for(int i=1;i<(int)nums.size();i++) {
                ans=max(ans,nums[i]-cmin);
                cmin=min(cmin,nums[i]);
            }
            return ans;
        }
    };
    

    股票交易

    推一波式子,然后单调队列优化即可。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    
    const int N=2000+5;
    
    int n,m,w;
    int ap[N],bp[N],as[N],bs[N];
    int f[N][N];
    // f[i][j] 表示第i天有操作并且操作完后剩下j支股票的最大收益。
    int g[N],q[N]; 
    void relax(int &a,const int &b) { a=((a>b) ? a : b); }
    
    int main()
    {
    //	freopen("1.in","r",stdin);
    	int i,j,k;
    	scanf("%d%d%d",&n,&m,&w);
    	for(i=1;i<=n;i++) 
    		scanf("%d%d%d%d",&ap[i],&bp[i],&as[i],&bs[i]);
    	memset(f,-0x3f,sizeof f);
    	memset(g,-0x3f,sizeof g);
    	g[0]=0;
    	for(i=0;i<=n;i++) f[i][0]=0;
    	
    	int hh,tt;
    	for(i=1;i<=n;i++) {
    		// 把 i-w-1 天的决策加入集合 
    		k=i-w-1;
    		if(k>0) {
    			for(j=0;j<=m;j++) 
    				g[j]=max(g[j],f[k][j]);
    		}
    		// buy
    		hh=tt=0,q[hh]=0;
    		for(j=1;j<=m;j++) {
    			while(hh<=tt&&q[hh]<j-as[i]) hh++;
    			relax(f[i][j],g[q[hh]]+q[hh]*ap[i]-j*ap[i]);
    			while(hh<=tt&&g[q[tt]]+q[tt]*ap[i]<=g[j]+j*ap[i]) tt--;
    			q[++tt]=j;
    		}
    		// sell
    		hh=tt=0,q[hh]=m;
    		for(j=m-1;j>=0;j--) {
    			while(hh<=tt&&q[hh]>j+bs[i]) hh++;
    			relax(f[i][j],g[q[hh]]+q[hh]*bp[i]-j*bp[i]);
    			while(hh<=tt&&g[q[tt]]+q[tt]*bp[i]<=g[j]+j*bp[i]) tt--;
    			q[++tt]=j;
    		}
    			
    //		for(j=0;j<=m;j++) {
    //			for(r=max(j-as[i],0);r<j;r++) relax(f[i][j],g[r]+(r-j)*ap[i]);
    //			for(r=j+1;r<=min(m,j+bs[i]);r++) relax(f[i][j],g[r]+(r-j)*bp[i]);
    //		}
    //		printf("f[%d][%d] = %d
    ",i,j,f[i][j]);
    	}
    	
    	int ans=0;
    	for(i=0;i<=n;i++) 
    		relax(ans,f[i][0]);
    	printf("%d
    ",ans);
    //	printf("%d
    ",f[n][0]);
    	return 0;
    }
    

    股票买卖

    #include<cstdio>
    #include<iostream>
    typedef long long LL;
    typedef unsigned long long ULL;
    using namespace std;
    const int INF=(-1u)>>1;
    const int N=1e5+5;
    int a[N];
    int n;
    int main()
    {
    //	freopen("1.in","r",stdin);
    	int i;
    	int ans=0,cmin;
    	scanf("%d",&n);
    	for(i=1;i<=n;i++) 
    		scanf("%d",&a[i]);
    	cmin=a[1];
    	for(i=2;i<=n;i++) {
    		ans=max(ans,a[i]-cmin);
    		cmin=min(cmin,a[i]);
    	}
    	cout<<ans;
    	return 0;
    }
    

    股票买卖 II

    #include<cstdio>
    #include<iostream>
    typedef long long LL;
    typedef unsigned long long ULL;
    using namespace std;
    const int N=1e5+5;
    LL a[N],f[N],cmax;
    int n;
    int main()
    {
    //	freopen("1.in","r",stdin);
    	int i;
    	scanf("%d",&n);
    	for(i=1;i<=n;i++) 
    		scanf("%lld",&a[i]);
    	cmax=-a[1];
    	for(i=2;i<=n;i++) {
    		f[i]=max(f[i-1],a[i]+cmax);
    		cmax=max(cmax,f[i-1]-a[i]);
    	}
    //	for(i=1;i<=n;i++) 
    //		cout<<f[i]<<" ";
    	cout<<f[n];
    	return 0;
    }
    

    股票买卖 III

    #include<cstdio>
    #include<iostream>
    typedef long long LL;
    typedef unsigned long long ULL;
    using namespace std;
    const int N=1e5+5;
    int f[N],g[N],a[N];
    int n;
    int main()
    {
    //	freopen("1.in","r",stdin);
    	int i;
    	scanf("%d",&n);
    	for(i=1;i<=n;i++) 
    		scanf("%d",&a[i]);
    	int cmin=a[1];
    	for(i=2;i<=n;i++) {
    		f[i]=max(f[i-1],a[i]-cmin);
    		cmin=min(a[i],cmin);
    	}
    	int cmax=a[n];
    	for(i=n-1;i>=1;i--) {
    		g[i]=max(g[i+1],cmax-a[i]);
    		cmax=max(cmax,a[i]);
    	}
    	int ans=0;
    	for(i=2;i<=n-1;i++) 
    		ans=max(ans,f[i]+g[i]);
    	cout<<ans;
    	return 0;
    }
    

    注意到 (dalao) 有跟我不一样的解法: https://www.acwing.com/solution/content/5049/

    股票买卖 IV

    #include<cstdio>
    #include<iostream>
    typedef long long LL;
    typedef unsigned long long ULL;
    using namespace std;
    const int N=1e5+5,K=105;
    int f[N][K],a[N],cmax[K];
    int n,m;
    // f[i][k] 表示选完了第i天的股票,所得到的最大利润,(第i天可选可不选) ;
    // f[i][k]=max(f[i-1][k],f[j][k-1]+a[i]-a[j]), 0<j<i;
    int main()
    {
    //	freopen("1.in","r",stdin);
    	int i,k;
    	scanf("%d%d",&n,&m);
    	for(i=1;i<=n;i++) 
    		scanf("%d",&a[i]);
    	fill(cmax,cmax+m+1,-a[1]);
    	for(i=2;i<=n;i++) {
    		for(k=m;k>=1;k--) { 
    			f[i][k]=max(f[i-1][k],a[i]+cmax[k-1]);
    			cmax[k]=max(cmax[k],f[i][k]-a[i]);
    		}
    		cmax[0]=max(cmax[0],-a[i]);
    	}
    	int ans=0;
    	for(i=1;i<=n;i++) 
    		for(k=0;k<=m;k++) 
    			ans=max(ans,f[i][k]);//,cout<<i<<" "<<k<<":"<<f[i][k]<<endl;
    	cout<<ans;
    	return 0;
    }
    

    转移方程:(f[i][k]=max(f[i-1][k],f[j][k-1]+a[i]-a[j]),j in [1,i-1])

    考虑到:(f[j][k-1]-a[j],j in [1,i-1])(i) 无关。

    (cmax[k]=max(f[j][k]-a[j]),j in [0,i-1]) .

    (f[i][k]=max(f[i-1][k],cmax[k]+a[i]));

    动态维护 (cmax[]) 即可 。

    注意:

    • 倒序:可以把 (cmax) 降成一维。
    • 另外维护 cmax[0];

    另一种解法:https://www.acwing.com/solution/content/5055/

    股票买卖 V

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+5;
    int f[N][2][2],a[N];
    int n;
    int main()
    {
    //	freopen("1.in","r",stdin);
        int i;
        scanf("%d",&n);
        f[0][1][1]=f[0][1][0]=f[0][0][1]=-1e9;
        for(i=1;i<=n;i++) 
            scanf("%d",&a[i]);
        for(i=1;i<=n;i++) {
            f[i][1][1]=f[i-1][0][0]-a[i];
            f[i][0][1]=max(f[i-1][1][1],f[i-1][1][0])+a[i];
            f[i][0][0]=max(f[i-1][0][0],f[i-1][0][1]);
            f[i][1][0]=max(f[i-1][1][0],f[i-1][1][1]);
        }
        cout<<max(max(f[n][1][1],f[n][0][0]),max(f[n][1][0],f[n][0][1]));
        return 0;
    }
    

    股票买卖 VI

    #ifdef cjlworld
    f[i] ---> 第i天能获得的最大利润
    f[i] = max ---> f[i-1]  // 什么事都不干
    	       ---> f[j]+a[i]-a[j]-f , 1<=j<=i-1; // j买 i卖 
    #endif
    #include<cstdio>
    #include<iostream>
    typedef long long LL;
    typedef unsigned long long ULL;
    using namespace std;
    const int N=1e5+5;
    int a[N],w;
    int n;
    int f[N],cmax; 
    int main()
    {
    //	freopen("1.in","r",stdin);
    	int i;
    	scanf("%d%d",&n,&w);
    	for(i=1;i<=n;i++) 
    		scanf("%d",&a[i]);
    	cmax=-a[1]-w;
    	for(i=2;i<=n;i++) {
    		f[i]=max(f[i-1],a[i]+cmax);
    		cmax=max(cmax,f[i]-a[i]-w);
    	}
    	cout<<f[n];
    	return 0;
    }
    

    低买

    最长下降子序列 以及 最长下降子序列计数。

    严格来讲不算是股票买卖的题目。

    Solution:

    • (f[i]) 代表以 (i) 结尾的最长下降子序列长度。

    • (g[i]) 代表以 (i) 结尾的最长下降子序列个数。(具体而言,就是 (f[i]) 能由几个 (f[j]) 转移而来);

    • (f[i]) 就不讲了。对于一般的题目 (g[i]= sum_{j=0}^{i-1} [a[j]>a[i]&&f[j]+1==f[i]]*g[j]);

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

    • 显然需要改变,对于相同的 (a[j1])(a[j2]) (假设 (f[i]) 都可以通过 (f[j1]) (f[j2]) 转移得来);

    • (j1) , (j2) 是有交集的。

    • 但是对于同一个值中可转移的方案取 (j) 的最大值显然就包含的其他的方案。

    • 因为其他方案可以通过只将最后一位换成 (j) 变成 (j) 的方案。

    • 倒序。

    Code:

    #include<set>
    #include<cstdio>
    #include<iostream>
    typedef long long LL;
    typedef unsigned long long ULL;
    using namespace std;
    const int N=5000+5;
    int a[N],f[N];
    LL g[N];
    int n;
    set<int> S;
    int main()
    {
    //	freopen("1.in","r",stdin);
    	int i,j;
    	scanf("%d",&n);
    	for(i=1;i<=n;i++) 
    		scanf("%d",&a[i]);
    	a[0]=1e9+5;
    	for(i=1;i<=n;i++) {
    		for(j=0;j<=i-1;j++) 
    			if(a[j]>a[i]) 
    				f[i]=max(f[i],f[j]+1);
    	}
    //	for(i=1;i<=n;i++) 
    //		cout<<f[i]<<" ";
    //	cout<<endl;
    //	for(i=2;i<=n;i++) 
    //		f[i]=max(f[i],f[i-1]);
    	g[0]=1;
    	for(i=1;i<=n;i++) {
    		S.clear();
    		for(j=i-1;j>=0;j--) {
    			if(a[i]<a[j]&&f[i]==f[j]+1&&(!S.count(a[j]))) {
    				g[i]+=g[j];
    				S.insert(a[j]);
    			}
    		}
    	}
    	int ans1=0; LL ans2=0;
    	for(i=1;i<=n;i++) 
    		ans1=max(ans1,f[i]);
    	S.clear();
    	for(i=n;i>=1;i--) 
    		if(f[i]==ans1&&(!S.count(a[i])))
    			ans2+=g[i],S.insert(a[i]);
    //	cout<<f[n]<<" "<<g[n];
    	cout<<ans1<<" "<<ans2;
    	return 0;
    }
    

    时间复杂度:(O(n^2logn))

    另一种做法:https://www.acwing.com/solution/content/5093/

    这种是一发现交集就把 (j) 较大的前半部分去掉。

    1163. 纪念品

    多支股票的股票买卖问题.
    如果沿用上述状态机定义,问题不能得到良好解决。
    但股票买卖问题有一个很好的性质,就是如果你把状态定义成全卖了,在后来的转移(第i天)时再判断第j天是否买入。
    那么 (假设在第k天第一次买入了股票)收益为 (a[i]-a[j]+a[j]-a[k]=a[i]-a[k]) 等价于在第k天买,第i天卖。
    因此第i天的状态只要考虑第 i-1 天的,就可以覆盖所有方案。

    剩下就不难了。

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<ctime>
    #include<cmath>
    #include<bitset>
    #include<vector>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    
    const int N=256,M=1e4+5;
    
    int day,n,m;
    int f[N]; // f[i] 表示在第i天,把所有纪念品都卖光的所获得的最大收入。
    int a[N][N]; // a[i][j] 表示第 i 天第 j 种纪念品的价格。
    int g[M];
    
    int main()
    {
    //	freopen("1.in","r",stdin);
    	int i,j,k;
    	int x,y;
    	
    	cin>>day>>n>>m;
    	for(i=1;i<=day;i++) 
    		for(j=1;j<=n;j++) 
    			cin>>a[i][j];
    
    	f[1]=m;
    	for(i=2;i<=day;i++) {
    		f[i]=f[i-1]; // 这一天啥也不干。 
    		
    		memset(g,0,sizeof g); // 背包的辅助数组。
    		// 背包问题 
    		for(j=1;j<=n;j++) {
    			if(a[i][j]>a[i-1][j]&&a[i-1][j]<=f[i-1]) 
    				x=a[i][j]-a[i-1][j],y=a[i-1][j]; // 价值和体积。 
    			else continue;
    			
    			for(k=y;k<=f[i-1];k++) 
    				g[k]=max(g[k],g[k-y]+x);
    		}
    		f[i]=max(f[i],g[f[i-1]]+f[i-1]);
    //		cout<<i<<" : "<<f[i]<<endl;
    	}
    	
    	cout<<f[day]<<endl;
    	return 0;
    }
    
  • 相关阅读:
    学习进度条73
    学习进度条72
    学习进度条71
    学习进度条70
    学习进度条69
    学习进度条68
    学习进度条67
    学习进度条66
    学习进度条65
    elasticsearch
  • 原文地址:https://www.cnblogs.com/cjl-world/p/13553538.html
Copyright © 2011-2022 走看看