【暑假】[深入动态规划]UVa 1412 Fund Management

UVa 1412 Fund Management

题目:

UVA - 1412

Fund Management

Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu

Submit Status

Description

Frank is a portfolio manager of a closed-end fund for Advanced Commercial Markets (ACM ). Fund collects money (cash) from individual investors for a certain period of time and invests cash into various securities in accordance with fund‘s investment strategy. At the end of the period all assets are sold out and cash is distributed among individual investors of the fund proportionally to their share of original investment.

Frank manages equity fund that invests money into stock market. His strategy is explained below.

Frank‘s fund has collected c<tex2html_verbatim_mark> US Dollars (USD) from individual investors to manage them for m<tex2html_verbatim_mark> days. Management is performed on a day by day basis. Frank has selected n<tex2html_verbatim_mark> stocks to invest into. Depending on the overall price range and availability of each stock, a lot size was chosen for each stock -- the number of shares of the stock Frank can buy or sell per day without affecting the market too much by his trades. So, if the price of the stock is pi<tex2html_verbatim_mark> USD per share and the lot size of the corresponding stock is si<tex2html_verbatim_mark> , then Frank can spend pisi<tex2html_verbatim_mark> USD to buy one lot of the corresponding stock for his fund if the fund has enough cash left, thus decreasing available cash in the fund. This trade is completely performed in one day.

When price of the stock changes to p‘i<tex2html_verbatim_mark> later, then Frank can sell this lot for p‘isi<tex2html_verbatim_mark> USD, thus increasing available cash for further trading. This trade is also completely performed in one day. All lots of stocks that are held by the fund must be sold by the end of the fund‘s period, so that at the end (like at the beginning) the fund is holding only cash.

Each stock has its own volatility and risks, so to minimize the overall risk of the fund, for each stock there is the maximum number of lots ki<tex2html_verbatim_mark> that can be held by the fund at any given day. There is also the overall limit k<tex2html_verbatim_mark> on the number of lots of all stocks that the fund can hold at any given day.

Any trade to buy or sell one lot of stock completely occupies Frank‘s day, and thus he can perform at most one such trade per day. Frank is not allowed to buy partial lots if there is not enough cash in the fund for a whole lot at the time of purchase.

Now, when fund‘s period has ended, Frank wants to know what is the maximum profit he could have made with this strategy having known the prices of each stock in advance. Your task is to write a program to find it out.

It is assumed that there is a single price for each stock for each day that Frank could have bought or sold shares of the stock at. Any overheads such as fees and commissions are ignored, and thus cash spent to buy or gained on a sell of one lot of stock is exactly equal to its price on this day multiplied by the number of shares in a lot.

Input

Input consists on several datasets. The first line of each dataset contains four numbers -- c<tex2html_verbatim_mark> , m<tex2html_verbatim_mark> , n<tex2html_verbatim_mark> , and k<tex2html_verbatim_mark> . Here c<tex2html_verbatim_mark>(0.01c100000000.00)<tex2html_verbatim_mark> is the amount of cash collected from individual investors up to a cent (up to two digits after decimal point); m<tex2html_verbatim_mark>(1m100)<tex2html_verbatim_mark> is the number of days in the fund‘s lifetime; n<tex2html_verbatim_mark>(1n8)<tex2html_verbatim_mark> is the number of stocks selected by Frank for trading;k<tex2html_verbatim_mark>(1k8)<tex2html_verbatim_mark> is the overall limit on the number of lots the fund can hold at any time.

The following 2n<tex2html_verbatim_mark> lines describe stocks and their prices with two lines per stock.

The first line for each stock contains the stock name followed by two integer numbers si<tex2html_verbatim_mark> and ki<tex2html_verbatim_mark> . Here si<tex2html_verbatim_mark>(1si1000000)<tex2html_verbatim_mark> is the lot size of the given stock, and ki<tex2html_verbatim_mark>(1kik)<tex2html_verbatim_mark> is the number of lots of this stock the fund can hold at any time. Stock name consists of 1 to 5 capital Latin letters from `` A" to `` Z". All stock names in the input file are distinct.

The second line for each stock contains m<tex2html_verbatim_mark> decimal numbers separated by spaces that denote prices of the corresponding stock for each day in the fund‘s lifetime. Stock prices are in range from 0.01 to 999.99 (inclusive) given up to a cent (up to two digits after decimal point).

Cash and prices in the input file are formatted as a string of decimal digits, optionally followed by a dot with one or two digits after a dot.

Output

For each dataset, write to the output file m + 1<tex2html_verbatim_mark> lines. Print a blank line between datasets.

On the first line write a single decimal number -- the precise value for the maximal amount of cash that can be collected in the fund by the end of its period. The answer will not exceed 1 000 000 000.00. Cash must be formatted as a string of decimal digits, optionally followed by a dot with one or two digits after a dot.

On the following m<tex2html_verbatim_mark> lines write the description of Frank‘s actions for each day that he should have made in order to realize this profit. Write BUY followed by a space and a stock name for buying a stock. Write SELL followed by a space and a stock name for selling a stock. Write HOLD if nothing should have been done on that day.

Sample Input

144624.00 9 5 3
IBM 500 3
97.27 98.31 97.42 98.9 100.07 98.89 98.65 99.34 100.82
GOOG 100 1
467.59 483.26 487.19 483.58 485.5 489.46 499.72 505 504.28
JAVA 1000 2
5.54 5.69 5.6 5.65 5.73 6 6.14 6.06 6.06
MSFT 250 1
29.86 29.81 29.64 29.93 29.96 29.66 30.7 31.21 31.16
ORCL 300 3
17.51 17.68 17.64 17.86 17.82 17.77 17.39 17.5 17.3

Sample Output

151205.00
BUY GOOG
BUY IBM
BUY IBM
HOLD
SELL IBM
BUY MSFT
SELL MSFT
SELL GOOG
SELL IBM

思路:

一共有n天,把天数看作阶段,对于每一天,我们可以选择出手或买进一手股票,在最后一天必须将股票全部出手且求解最大钱数。

可以这样定义d[i][s]:表示第i天手中股票的状态为s时手中的最大钱数,采用刷表法更新d[i+1][s‘] ,s‘表示s经过出手或买进转移的状态。

问题就变成了如何表示状况s?采用n元组的形式。

但不能将一个n元组表示进d数组,这里的方法是离线dfs出全部状态并分别编号,得出状态与相连的关系buy_next与sell_next。那么d中的状态s就可以用一个整数表示了。

另外输出也有一定的技巧,用到了prev 与 opt 数组,并用正负区别操作。

代码(from Rujia Liu):

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<vector>
  4 #include<map>
  5 using namespace std;
  6
  7 const double INF = 1e30;
  8 const int maxn = 8;
  9 const int maxm = 100 + 5;
 10 const int maxstate = 15000;
 11
 12 int m, n, s[maxn], k[maxn], kk;
 13 double c, price[maxn][maxm];
 14 char name[maxn][10];
 15
 16 double d[maxm][maxstate];
 17 int opt[maxm][maxstate], prev[maxm][maxstate];    //配合输出print_ans
 18
 19 int buy_next[maxstate][maxn], sell_next[maxstate][maxn];
 20 vector<vector<int> > states;
 21 //states[i]代表一个标号为i的n元组  组信息用vector保存 //一个n元组带包手持各股票的数目
 22 map<vector<int>, int> ID;                        //ID 是vector到序号的映射
 23
 24 void dfs(int stock, vector<int>& lots, int totlot) {  //dfs序构造states
 25   if(stock == n) {              //新的n元组构造完成
 26     ID[lots] = states.size();   //ID
 27     states.push_back(lots);     //push
 28   }
 29   else for(int i = 0; i <= k[stock] && totlot + i <= kk; i++) {   //在满足k[]与K的限制下如果可行则dfs下一stock
 30     lots[stock] = i;
 31     dfs(stock+1, lots, totlot + i);    //回溯 写法
 32   }
 33 }
 34
 35 void init() {    //利用states离线建立状态之间的关系
 36   vector<int> lots(n);
 37   states.clear();  //clear1
 38   ID.clear();      //clear2
 39   dfs(0, lots, 0);  //return states
 40   for(int s = 0; s < states.size(); s++) {   //操作一个状态
 41     int totlot = 0;
 42     for(int i = 0; i < n; i++) totlot += states[s][i];  //目前状态的所有股数
 43     for(int i = 0; i < n; i++) {             //枚举在状态中改变的股票i
 44       buy_next[s][i] = sell_next[s][i] = -1;   //初值-1
 45       if(states[s][i] < k[i] && totlot < kk) { //如果buy可行
 46         vector<int> newstate = states[s];
 47         newstate[i]++;
 48         buy_next[s][i] = ID[newstate];
 49       }
 50       if(states[s][i] > 0) {                //如果sell可行
 51         vector<int> newstate = states[s];
 52         newstate[i]--;
 53         sell_next[s][i] = ID[newstate];
 54       }
 55     }
 56   }
 57 }
 58
 59 void update(int day, int s, int s2, double v, int o) {  //刷表法 更新
 60 //在第day天 在进行操作后 状况s转移到状况s2 转移后手中钱数为v
 61 //对|o|进行操作      //opt的正负用以区分操作 buy || sell
 62   if(v > d[day+1][s2]) {
 63     d[day+1][s2] = v;
 64     opt[day+1][s2] = o;    //: 得出 [][] 的最优操作
 65     prev[day+1][s2] = s;   //: 得出 [][] 的最优前状况
 66   }
 67 }
 68
 69 double dp() {
 70   for(int day = 0; day <= m; day++)
 71     for(int s = 0; s < states.size(); s++) d[day][s] = -INF //边界设定
 72
 73   d[0][0] = c;          //第0天手持0手股票 手中有c的钱数
 74   for(int day = 0; day < m; day++)              //枚举天数
 75     for(int s = 0; s < states.size(); s++) {    //枚举手中股票的状态
 76       double v = d[day][s];
 77       if(v < -1) continue;  //return
 78
 79       update(day, s, s, v, 0); // HOLD
 80       for(int i = 0; i < n; i++) {
 81         if(buy_next[s][i] >= 0 && v >= price[i][day] - 1e-3)       //s状态下要买股票i
 82           update(day, s, buy_next[s][i], v - price[i][day], i+1); // BUY
 83         if(sell_next[s][i] >= 0)                                   //s状态下要卖股票i
 84           update(day, s, sell_next[s][i], v + price[i][day], -i-1); // SELL
 85       }
 86     }
 87   return d[m][0];    //到了第m天 手中没有股票 //反对DP原问题的最大值
 88 }
 89
 90 void print_ans(int day, int s) {   //根据prev与opt递归输出解
 91   if(day == 0) return;
 92   print_ans(day-1, prev[day][s]);
 93   if(opt[day][s] == 0) printf("HOLD\n");   //==0
 94   else if(opt[day][s] > 0) printf("BUY %s\n", name[opt[day][s]-1]);  // >0
 95   else printf("SELL %s\n", name[-opt[day][s]-1]);                    //<0
 96 }
 97
 98 int main() {
 99   int kase = 0;
100   while(scanf("%lf%d%d%d", &c, &m, &n, &kk) == 4) {
101     if(kase++ > 0) printf("\n");
102
103     for(int i = 0; i < n; i++) {
104       scanf("%s%d%d", name[i], &s[i], &k[i]);
105       for(int j = 0; j < m; j++) { scanf("%lf", &price[i][j]); price[i][j] *= s[i]; }
106     }
107     init();
108
109     double ans = dp();
110     printf("%.2lf\n", ans);
111     print_ans(m, 0);
112   }
113   return 0;
114 }
时间: 2024-11-06 10:21:57

【暑假】[深入动态规划]UVa 1412 Fund Management的相关文章

UVA 1412 - Fund Management(用vector容器模拟状态的状压dp)

Frank is a portfolio manager of a closed-end fund for Advanced Commercial Markets (ACM ). Fund collects money (cash) from individual investors for a certain period of time and invests cash into various securities in accordance with fund's investment

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.单位是美元/股)与参数s

uva1412 Fund Management

状压dp 要再看看  例题9-17 /* // UVa1412 Fund Management // 本程序会超时,只是用来示范用编码/解码的方法编写复杂状态动态规划的方法 // Rujia Liu #include<cstdio> #include<cstring> #include<map> using namespace std; const double INF = 1e30; const int maxn = 8; const int maxm = 100 +

【暑假】[深入动态规划]UVa 12170 Easy Climb

UVa 12170 Easy Climb 题目: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=24844 思路:  引别人一个题解琢磨一下: from:http://blog.csdn.net/glqac/article/details/45257659 代码: 1 #include<iostream> 2 #include<algorithm> 4 #define FOR(a,b,c) for(int a

【暑假】[深入动态规划]UVa 10618 Fixing the Great Wall

UVa 10618 Fixing the Great Wall 题目:  http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=36139 思路:   数轴上有n个点需要修复,每个点有信息c,x,d 表示位于x且在t时修缮的费用是c+d*t,找一个修缮序列使n个点能全部修缮且有费用最小. 可以发现:在任意时刻,修缮完的点都是连续的,因为修缮不需要时间,将一些点“顺手”修缮了肯定不差. d[i][j][k],表示已经将i-j个点修缮

【暑假】[深入动态规划]UVa 1628 Pizza Delivery

UVa 1628 Pizza Delivery 题目: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=51189 思路:   本体与修缮长城一题有所相似.所以解法有相似之处. 不同之处就是本体可能会产生负情况,即送餐时间晚了客户会反过来找你要钱所以需要放弃,但修缮长城只有费用,顺手修了肯定是一个不错的选择. 依旧将区间两端与位置作为状态不过要添加一维cnt表示还需要送餐的人数.类似地定义:d[i][j][cnt][p]表示

【暑假】[深入动态规划]UVa 1627 Team them up!

UVa 1627 Team them up! 题目: Team them up! Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu Submit Status Description Your task is to divide a number of persons into two teams, in such a way, that: everyone belongs to one of th

【暑假】[深入动态规划]UVa 10618 The Bookcase

UVa 12099  The Bookcase 题目: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=42067 思路:   将n本书分配到三层,使得形成的书架w*h最小 提前将书籍按照高度排序,因为无论第一本书(最高的书)无论放在那一层都会被考虑到,所以规定将它放在第一层,且第二层比第三层高. 因为从大到小排序的关系,只要jk==0那么新加入的书i就是该层的高度,否则高度不变. 设d[i][j][k]表示考虑过i本书第二

【暑假】[深入动态规划]UVa 10618 Tango Tango Insurrection

UVa 10618 Tango Tango Insurrection 题目: Problem A: Tango Tango Insurrection You are attempting to learn to play a simple arcade dancing game. The game has 4 arrows set into a pad: Up, Left, Down, Right. While a song plays, you watch arrows rise on a s