UVa 1412 - Fund Management(狀壓DP + 預處理)
阿新 • • 發佈:2018-10-10
out code clas https continue amp 註意 emp 最後一天
每天要麽不操作,要麽選一只股票,買或賣它的一手股票。c和股價均最多包含兩位小數(即美分)。
最優解保證不超過1e9。要求輸出每一天的決策(HOLD表示不變,SELL表示賣,BUY表示買)。
因為幾乎每次狀態轉移都會涉及編碼、解碼操作,狀態轉移的時間大幅度提升,最終導致超時。
解決方法是事先計算出所有可能的狀態並且編號,然後構造一個狀態轉移表,
用buy[s][i]和sell[s][i]分別表示狀態s進行“買股票i”和“賣股票i”之後轉移到的狀態編號。
動態規劃主程序采用刷表法,為了方便起見,另外編寫了“更新狀態”的函數update。
為了打印解,在更新解d時還要更新最優策略opt和“上一個狀態”f。
註意代碼中的price[i][day]表示第day天時一手股票i的價格,而不是輸入中的“每股價格”。
最後是打印解的部分。因為狀態從前到後定義,因此打印解時需要從後到前打印,用遞歸比較方便。
鏈接:
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 }
UVa 1412 - Fund Management(狀壓DP + 預處理)