UVa1412基金管理

题意

在这里插入图片描述

思路:

本题的基本思路是明确的,用d(i,p)表示经过 i 天之后,资产组合为p时的现金的最大值。
另外值得注意的是,本题在考虑买股票时要考虑到当前拥有的现金是否足够,因此不是一个DAG最长/最短路问题,因为某些边u->v的存在性依赖于起点到点u的最短路值。也就是说,本题不能像之前的DAG问题一样“反着定义”:如果用d(i,p)表示资产组合为p,从第 i 天开始到最后能拥有的现金最大值,会发现状态根本无法转移。

  1. 9进制处理资金组合状态
  2. 状态编号,不必编码解码
  3. 刷表法
#include <cstdio>
#include <vector>
#include <map> 
#include <algorithm>
using namespace std;
const int INF = 1<<30;

const int maxn = 8;
const int maxm = 101;
const int maxstate = 15000; 

double c, price[maxn][maxm];
int m, n, kk;
char name[maxn][50];
int s[maxn], k[maxn];

vector< vector<int> > states; // states[i]:状态 i 
map< vector<int>, int > ID; // 状态对应的编号

// 为每种状态编号 
void dfs(int cur_stock, vector<int> &s, int total){
	if(cur_stock == n){
		ID[s] = states.size();
		states.push_back( s );
		return;
	}
	// 对当前股票买 i 手 
	for(int i = 0; i <= k[cur_stock] && total + i <= kk; ++i){
		s[cur_stock] = i;
		dfs(cur_stock + 1, s, total + i);
	}
} 

int buy_next[maxstate][maxn], sell_next[maxstate][maxn]; // 分别表示状态 s 进行"买股票i”和“卖股票i”之后转移到的状态编号 
void init(){
	vector<int> s1(n);
	states.clear();
	ID.clear();
	// 给所有股票状态编号 
	dfs(0, s1, 0);
	// 求buy_next, sell_next 
	for(int s = 0; s < states.size(); ++s){
		int total = 0;
		for(int i = 0; i < n; ++i) total += states[s][i];
		for(int i = 0; i < n; ++i){
			buy_next[s][i] = sell_next[s][i] = -1;
			// 买第 i支股票 
			if(states[s][i] < k[i] && total < kk){
				vector<int> newstate = states[s];
				++newstate[i];
				buy_next[s][i] = ID[newstate];
			}
			// 卖第 i支股票 
			if(states[s][i] > 0){
				vector<int> newstate = states[s];
				--newstate[i];
				sell_next[s][i] = ID[newstate];
			}
		} 
	}
}

double d[maxm][maxstate];
int opt[maxm][maxstate], pre[maxm][maxstate];
// 当前时间(day)的状态是s,它的操作op可能给下一个状态s2带来影响,如果它的决策好,及时更新 
void update(int day, int s, int s2, double v, int op){
	if(v > d[day+1][s2]){
		d[day+1][s2] = v;
		opt[day+1][s2] = op;
		pre[day+1][s2] = s;
	}
}
// 规划 
double dp(){
	// 初始化 
	for(int day = 0; day <= m; ++day)
		for(int s = 0; s < states.size(); ++s)
			d[day][s] = -INF;
	//printf("%d\n",states.size());
	d[0][0] = c;
	for(int day = 0; day < m; ++day){
		for(int s = 0; s < states.size(); ++s){
			double v = d[day][s];
			if(v < -1) continue;
			// hold
			update(day, s, s, v, 0);
			for(int i = 0; i < n; ++i){
				if(buy_next[s][i] >= 0 && v >= price[i][day] - 1e-3) 
					update(day, s, buy_next[s][i], v - price[i][day], i+1); // buy ith stock
				if(sell_next[s][i] >= 0)
					update(day, s, sell_next[s][i], v + price[i][day], -i-1); // sell ith stock
			}
		}
	}
	return d[m][0];
}

void print_ans(int day, int s){
	if(day == 0) return;
	print_ans(day-1, pre[day][s]);
	if(opt[day][s] == 0) printf("HOLD\n");
	else if(opt[day][s] > 0) printf("BUY %s\n",name[opt[day][s]-1]);
	else printf("SELL %s\n",name[-opt[day][s]-1]);
}

/* 
测试方案总数 
int cnt = 0;
void test(int cur, int total){
	if(cur == 8){
		++cnt;
		return;
	}
	for(int i = 0; i <= 8 && total+i <= 8; ++i){
		test(cur+1, total+i);
	}
}
*/

int main()
{
	//freopen("in.txt","r",stdin);
	// test(0,0);
	// printf("%d\n",cnt); 输出12870,方案总数不超过15000. 
	int kase = 0;
	while(scanf("%lf %d %d %d",&c, &m, &n, &kk) == 4){
		if(kase++ > 0) printf("\n");
		for(int i = 0; i < n; ++i){
			scanf("%s %d %d",name[i], &s[i], &k[i]);
			for(int j = 0; j < m; ++j) {
				scanf("%lf",&price[i][j]);
				price[i][j] *= s[i];
			}
		}
		init();
		double ans = dp();
	    printf("%.2lf\n", ans);
	    print_ans(m, 0);
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值