UVA 1412 Fund Management (复杂状态的DP,状态池)

UVA 1412 Fund Management (复杂状态的DP,状态池)

受到前面题目的影响,一开始的思路是用d(i,s)表示经过第i天后,选择了集合s里面的股票的当前最大现金。但是发股票可以选多手,根本无法用集合表示。

由于每只股票的可持有的手数为(0<=ki<=8),最多有8只股票,所以表示当前手持股票资产的理论状态数最多有98<5*107种,但是由于还有一个持有总手数k的最大限制,实际状态数将远远低于这个值。实际状态数<1.5e4(我也不知道为什么)

所以用d(i,p)表示经过i天后,当前手持股票资产为p时的最大现金数。因为每只股票可持有的手数为0~8,所以可以用一个9进制的数去表示一个状态p。但是在状态转移的过程中,需要对整数p进行解码、编码的操作十分费时,对于这道题会导致TLE。

借鉴紫书上的做法,先将所有的状态数枚举出来并进行标号储存,这样我们就可以建立起一个状态–自然数的映射。并将每个状态可转移到的状态也通过预处理枚举并储存,这样就大大减少了dp过程的时间。

同时因为股票只能一手一手的买,我们可以将一手的价格预处理出来,这样可以避免重复的运算。

这道理的状态转移过程是十分明了的,可以从题目一眼看是,对于第i天,可以保持(HOLD),可以选择一只股票进行买一手(BUY)或者卖一手(SELL)。

由于是否可以买股票要取决于当前的现金是否足够,所以需要判断。所以这道题并不是DAG上的动态规划,因为edge(u,v)是否存在取决于之前的决策。所以不可以像DAG那样逆着去定义状态:如果用d(i,p)去表示从第i天开始到最后能拥有的最大现金值,那么状态将无法转移,因为不能够判断对于第i-1天,是否有足够的钱去买某个股票的一手。

具体实现上:
用一个vector去表示一个状态,那么可以用一个大vector来作状态池来储存状态,并将每个状态对应的下标作为标号用map建立起映射(ID)。
用buy_next[p][i]来表示在p状态购买了i股票后转移到的状态的编号(合法的),同理用sell_next[p][i]来表示在p状态卖出了i股票后转移到的状态的编号(合法的)。
因为需要打印答案,用pre[i[[p]表示在d(i,p)前一个状态的标号,用opt[i][p]表示在d(i,p)的操作:
当opt[i][p]==0时,表示HOLD操作
当opt[i][p]>0时,表示BUY编号为opt[i][p]-1的股票。
当opt[i][p]<0时,表示SELL编号为-(opt[i][p]+1)的股票。
在打印时只需递归进行打印就好了。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int max_state=15000;
const int INF=-0x3f3f3f3f;
double c;
char name[10][30];
int si[10];
int ki[10];
double ci[10][105];
vector<vector<int> > state;
map<vector<int>,int> ID;
int buy_next[max_state][10];
int sell_next[max_state][10];
double dp[105][max_state];
int pre[105][max_state];
int opt[105][max_state];
int n,m,k;
void dfs(vector<int> &lot,int s,int total_k)
{
   
	if(s==n){
   
		ID[lot]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值