1. 程式人生 > >UVa 1412 - Fund Management(狀壓DP + 預處理)

UVa 1412 - Fund Management(狀壓DP + 預處理)

out code clas https continue amp 註意 emp 最後一天

鏈接:

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 + 預處理)