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的价格,而不是输入中的“每股价格”。
最后是打印解的部分。因为状态从前到后定义,因此打印解时需要从后到前打印,用递归比较方便。
代码:
1 #include <cstdio> 2 #include <map> 3 #include <vector> 4 using namespace std; 5 6 typedef long long int LLI; 7 const LLI INF = 0x3f3f3f3f3f3f3f3f; 8 const int UPM = 100 + 5; 9 const int UPN = 8 + 5; 10 const int UPS = 15000; 11 int m, n, kk, k[UPN]; 12 int buy[UPS][UPN], sell[UPS][UPN], f[UPM][UPS], opt[UPM][UPS]; 13 LLI c, price[UPN][UPM], d[UPM][UPS]; 14 char name[UPN][5+5]; 15 vector<vector<int> > state; 16 map<vector<int>,int> id; 17 18 void dfs(int stock, vector<int>& V, int tot) { 19 if(stock == n) { 20 id[V] = state.size(); 21 state.push_back(V); 22 return; 23 } 24 for(int i = 0; i <= k[stock] && tot+i <= kk; i++) { 25 V[stock] = i; 26 dfs(stock+1, V, tot+i); 27 } 28 } 29 30 void init() { 31 state.clear(); 32 id.clear(); 33 vector<int> V(n); 34 dfs(0, V, 0); 35 for(int s = 0; s < state.size(); s++) { 36 int tot = 0; 37 for(int i = 0; i < n; i++) tot += state[s][i]; 38 for(int i = 0; i < n; i++) { 39 buy[s][i] = sell[s][i] = -1; 40 if(state[s][i] < k[i] && tot < kk) { 41 V = state[s]; 42 V[i]++; 43 buy[s][i] = id[V]; 44 } 45 if(state[s][i] > 0) { 46 V = state[s]; 47 V[i]--; 48 sell[s][i] = id[V]; 49 } 50 } 51 } 52 } 53 54 void update(int day, int s, int s2, LLI v, int o) { 55 if(d[day+1][s2] >= v) return; 56 d[day+1][s2] = v; 57 f[day+1][s2] = s; 58 opt[day+1][s2] = o; 59 } 60 61 LLI dynamicProgramming() { 62 for(int i = 0; i <= m; i++) 63 for(int s = 0; s < state.size(); s++) d[i][s] = -INF; 64 d[0][0] = c; 65 for(int day = 0; day < m; day++) { 66 for(int s = 0; s < state.size(); s++) { 67 LLI v = d[day][s]; 68 if(v < -1) continue; 69 update(day, s, s, v, 0); 70 for(int i = 0; i < n; i++) { 71 if(buy[s][i] >= 0 && v-price[i][day] >= 0) 72 update(day, s, buy[s][i], v-price[i][day], i+1); 73 if(sell[s][i] >= 0) 74 update(day, s, sell[s][i], v+price[i][day], -(i+1)); 75 } 76 } 77 } 78 return d[m][0]; 79 } 80 81 void output(int day, int s) { 82 if(day == 0) return; 83 output(day-1, f[day][s]); 84 if(opt[day][s] == 0) printf("HOLD\n"); 85 else if(opt[day][s] > 0) printf("BUY %s\n", name[opt[day][s]-1]); 86 else printf("SELL %s\n", name[-opt[day][s]-1]); 87 } 88 89 int main() { 90 double temp; 91 int lot, cases = 0; 92 while(~scanf("%lf%d%d%d", &temp, &m, &n, &kk)) { 93 c = (temp + 1e-3) * 100; 94 for(int i = 0; i < n; i++) { 95 scanf("%s%d%d", name[i], &lot, &k[i]); 96 for(int t = 0; t < m; t++) { 97 scanf("%lf", &temp); 98 price[i][t] = (LLI)((temp + 1e-3) * 100) * lot; 99 } 100 } 101 init(); 102 LLI ans = dynamicProgramming(); 103 if(cases++ > 0) printf("\n"); 104 printf("%lld.%02lld\n", ans/100, ans%100); 105 output(m, 0); 106 } 107 return 0; 108 }