P1108 低价购买 (动态规划)

题目链接


Solution

似乎就是个很简单的最长不上升子序列输出方案.
但是有一个很艹蛋的条件: 不同方案选择价格必须不同.
且其股票价格不保证不相同.

\(f[i]\) 代表以第 \(i\) 天结尾的不上升子序列的长度.

其实我们可以推出一个条件 : 相同的两个股票价格同时出现,后者的方案里面一定包括前者的.
这也是我们按平常做法多出来的方案数.
所以我们可以直接在 DP 的时候记录一个 \(g[i][j]\) 代表在 \(i\) 时由最后一个价值为 \(j\) 的已经累加的次数.
然后每次加上的便是 \(g[v][j]-g[i][j]\). 其中 \(v\) 为可以转移到 \(i\) 最优方案的节点.

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=5008;
int w[maxn],n;
int f[maxn];
map<int,int>g[maxn];
map<int,int>v;
int ans,ans_num;
int main()
{
	scanf("%d",&n); w[0]=0x3f3f3f3f;
	for(int i=1;i<=n;i++)
	scanf("%d",&w[i]);
	g[0][w[0]]=1; f[0]=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<i;j++)
		if(w[i]<w[j])
		  	if(f[j]+1>f[i])
		    	f[i]=f[j]+1;
		
		for(int j=0;j<i;j++)
		if(w[i]<w[j])
			if(f[j]+1==f[i])
			{
				g[i][w[i]]+=(g[j][w[j]]-g[i][w[j]]);
				g[i][w[j]]=g[j][w[j]];
			}		
	}
	for(int i=1;i<=n;i++)
		if(f[i]>ans)ans=f[i];
	for(int i=1;i<=n;i++)
		if(f[i]==ans)
		{
			ans_num+=g[i][w[i]]-v[w[i]];
			v[w[i]]=g[i][w[i]];
		}
	cout<<ans<<' '<<ans_num;
	return 0;
}
posted @ 2018-09-19 10:19  Kevin_naticl  阅读(251)  评论(0编辑  收藏  举报