UVa 1412 - Fund Management(状压DP + 预处理)
链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4158
题意:
你有c(0.01≤c≤1e8)美元现金,但没有股票。给你m(1≤m≤100)天时间和n(1≤n≤8)支股票供你买卖,
要求最后一天结束后不持有任何股票,且剩余的钱最多。买股票不能赊账,只能用现金买。
已知每只股票每天的价格(0.01~999.99。单位是美元/股)与参数si和ki,
表示一手股票是si(1≤si≤1e6)股,且每天持有的手数不能超过ki(1≤ki≤k),其中k为每天持有的总手数上限。
每天要么不操作,要么选一只股票,买或卖它的一手股票。c和股价均最多包含两位小数(即美分)。
最优解保证不超过1e9。要求输出每一天的决策(HOLD表示不变,SELL表示卖,BUY表示买)。
分析:
以用d(i,p)表示经过i天之后,资产组合为p时的现金的最大值。其中p是一个n元组,pi≤ki表示第i只股票有pi手。
根据题目规定,p1+…+pn≤k。因为0≤pi≤8,理论上最多只有9^8<5e7种可能,所以可以用一个九进制整数来表示p。
一共有3种决策:HOLD、BUY和SELL,分别进行转移即可。
注意在考虑购买股票时不要忘记判断当前拥有的现金是否足够。
但是这样的做法效率不够高,因为九进制整数无法直接进行“买卖股票”的操作,需要解码成n元组才行。
因为几乎每次状态转移都会涉及编码、解码操作,状态转移的时间大幅度提升,最终导致超时。
解决方法是事先计算出所有可能的状态并且编号,然后构造一个状态转移表,
用buy[s][i]和sell[s][i]分别表示状态s进行“买股票i”和“卖股票i”之后转移到的状态编号。
动态规划主程序采用刷表法,为了方便起见,另外编写了“更新状态”的函数update。
为了打印解,在更新解d时还要更新最优策略opt和“上一个状态”f。
注意代码中的price[i][day]表示第day天时一手股票i的价格,而不是输入中的“每股价格”。
最后是打印解的部分。因为状态从前到后定义,因此打印解时需要从后到前打印,用递归比较方便。
代码:
#include <cstdio>
#include <map>
#include <vector>
using namespace std; typedef long long int LLI;
const LLI INF = 0x3f3f3f3f3f3f3f3f;
const int UPM = + ;
const int UPN = + ;
const int UPS = ;
int m, n, kk, k[UPN];
int buy[UPS][UPN], sell[UPS][UPN], f[UPM][UPS], opt[UPM][UPS];
LLI c, price[UPN][UPM], d[UPM][UPS];
char name[UPN][+];
vector<vector<int> > state;
map<vector<int>,int> id; void dfs(int stock, vector<int>& V, int tot) {
if(stock == n) {
id[V] = state.size();
state.push_back(V);
return;
}
for(int i = ; i <= k[stock] && tot+i <= kk; i++) {
V[stock] = i;
dfs(stock+, V, tot+i);
}
} void init() {
state.clear();
id.clear();
vector<int> V(n);
dfs(, V, );
for(int s = ; s < state.size(); s++) {
int tot = ;
for(int i = ; i < n; i++) tot += state[s][i];
for(int i = ; i < n; i++) {
buy[s][i] = sell[s][i] = -;
if(state[s][i] < k[i] && tot < kk) {
V = state[s];
V[i]++;
buy[s][i] = id[V];
}
if(state[s][i] > ) {
V = state[s];
V[i]--;
sell[s][i] = id[V];
}
}
}
} void update(int day, int s, int s2, LLI v, int o) {
if(d[day+][s2] >= v) return;
d[day+][s2] = v;
f[day+][s2] = s;
opt[day+][s2] = o;
} LLI dynamicProgramming() {
for(int i = ; i <= m; i++)
for(int s = ; s < state.size(); s++) d[i][s] = -INF;
d[][] = c;
for(int day = ; day < m; day++) {
for(int s = ; s < state.size(); s++) {
LLI v = d[day][s];
if(v < -) continue;
update(day, s, s, v, );
for(int i = ; i < n; i++) {
if(buy[s][i] >= && v-price[i][day] >= )
update(day, s, buy[s][i], v-price[i][day], i+);
if(sell[s][i] >= )
update(day, s, sell[s][i], v+price[i][day], -(i+));
}
}
}
return d[m][];
} void output(int day, int s) {
if(day == ) return;
output(day-, f[day][s]);
if(opt[day][s] == ) printf("HOLD\n");
else if(opt[day][s] > ) printf("BUY %s\n", name[opt[day][s]-]);
else printf("SELL %s\n", name[-opt[day][s]-]);
} int main() {
double temp;
int lot, cases = ;
while(~scanf("%lf%d%d%d", &temp, &m, &n, &kk)) {
c = (temp + 1e-) * ;
for(int i = ; i < n; i++) {
scanf("%s%d%d", name[i], &lot, &k[i]);
for(int t = ; t < m; t++) {
scanf("%lf", &temp);
price[i][t] = (LLI)((temp + 1e-) * ) * lot;
}
}
init();
LLI ans = dynamicProgramming();
if(cases++ > ) printf("\n");
printf("%lld.%02lld\n", ans/, ans%);
output(m, );
}
return ;
}
UVa 1412 - Fund Management(状压DP + 预处理)的更多相关文章
- UVa 1412 Fund Management (预处理+状压DP)
题意:题意很难说清楚自己看原文,链接:UVa 1412 Fund Management 析:总体来说如果没有超时的话,这个题不是特别难,但是这个题很容易超时,主要是体现在状态转移时,很容易想到状态方程 ...
- 【暑假】[深入动态规划]UVa 1412 Fund Management
UVa 1412 Fund Management 题目: UVA - 1412 Fund Management Time Limit: 3000MS Memory Limit: Unknown ...
- UVA 1412 Fund Management (预处理+状压dp)
状压dp,每个状态可以表示为一个n元组,且上限为8,可以用一个九进制来表示状态.但是这样做用数组开不下,用map离散会T. 而实际上很多九进制数很多都是用不上的.因此类似uva 1601 Mornin ...
- UVa 1204 Fun Game (状压DP)
题意:有一些小孩(至少两个)围成一圈,有 n 轮游戏,每一轮从某个小孩开始往左或者往右伟手帕,拿到手帕写上自己的性别(B,G),然后以后相同方向给下一个. 然后在某个小孩结束,给出 n 轮手帕上的序列 ...
- UVa 11825 Hackers' Crackdown (状压DP)
题意:给定 n 个计算机的一个关系图,你可以停止每台计算机的一项服务,并且和该计算机相邻的计算机也会终止,问你最多能终止多少服务. 析:这个题意思就是说把 n 台计算机尽可能多的分成一些组,使得每组的 ...
- bzoj 2734 [HNOI2012]集合选数 状压DP+预处理
这道题很神啊…… 神爆了…… 思路大家应该看别的博客已经知道了,但大部分用的插头DP.我加了预处理,没用插头DP,一行一行来,速度还挺快. #include <cstdio> #inclu ...
- UVA - 1252 Twenty Questions (状压dp+vis数组加速)
有n个物品,每个物品有m个特征.随机选择一个物品让你去猜,你每次可以询问一个特征的答案,问在采取最优策略时,最坏情况下需要猜的次数是多少. 设siz[S]为满足特征性质集合S的特征的物品总数,dp[S ...
- UVA 11825 Hackers’ Crackdown 状压DP枚举子集势
Hackers’ Crackdown Miracle Corporations has a number of system services running in a distributed com ...
- UVa 1252 Twenty Questions (状压DP+记忆化搜索)
题意:有n件物品,每件物品有m个特征,可以对特征进行询问,询问的结果是得知某个物体是否含有该特征,要把所有的物品区分出来(n个物品的特征都互不相同), 最小需要多少次询问? 析:我们假设心中想的那个物 ...
随机推荐
- LinqProvider系列(三)如何实现自己的Linq Provider?
这篇文章将在前人的肩上,继续完成实现Linq Provider的任务. 首先,我们列出linq语法的解析过程: linq本质上就是把我们惯用的语法糖,变成了一颗表达式树,然后由不同的linq Prov ...
- nginx正确服务react-router应用
如今React应用普遍使用react-router作为路由管理,在开发端webpack自带的express服务器下运行和测试表现均正常,部署到线上的nginx服务器后,还需要对该应用在nginx的配置 ...
- Django中间件解析
一,中间件的概念 django 中的中间件(middleware),在django中,中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法.在dja ...
- 使用镶嵌数据集 MosaicDataSet管理不同分辨率影像数据
镶嵌数据集 MosaicDataSet是Esri推出的一种用于管理海量影像数据的数据模型,它是Geodatabase数据模型的一个子集定义. 该数据模型强大之处在于它能统一管理不同采集时间.不同采集来 ...
- Qt编译目录下exe文件执行报错问题的解决办法
使用Qt5.9.3+vs2017环境,编译项目生成Debug目录,运行其中的exe文件,出现以下错误(qt creator调试状态下或出安装包后是可以运行的): 经过查阅资料,发现是我重新配置Qt开发 ...
- 记一次Android studio升级之后的坑
像往常一样打开Android studio,但这次它提示我升级!说是什么为了更好的体验,在好奇心的驱使下,我毅然地点击了“update”按钮.升级之后,编译项目,报出了N多个error,我的心都慌完! ...
- 填报表导出excel后不可写的单元格处于锁定状态
填报表单元格分为可写和不可写两种状态,当填报表在web上展现的时候可写单元格可以进行数据填报和修改,非可写单元格不可操作. 报表导出为excel时,润乾导出excel包默认情况下不对excel单 ...
- 润乾报表与DERBY数据库的创建连接详解
1. 问题概述 1.Derby数据库的创建过程 2.润乾报表连接Derby数据库展现数据 概述: Derby是Apache Software Foundation (ASF)的一个的孵化器项目. ...
- 【Python】Java程序员学习Python(三)— 基础入门
一闪一闪亮晶晶,满天都是小星星,挂在天上放光明,好像许多小眼睛.不要问我为什么喜欢这首歌,我不会告诉你是因为有人用口琴吹给我听. 一.Python学习文档与资料 一般来说文档的资料总是最权威,最全面的 ...
- LeetCode题解之Add two numbers
1.题目描述 2.题目描述 题目思路可以参考合并单链表的思路,定义一个全局 进位标志,如果两个数值相加得到需要进位,则将进位标志置为1 . 3.代码 ListNode* addTwoNumbers(L ...