题目链接:点击打开链接
【链接为NEERC 2007整套题,该题为其中的F题】
【要不是UVALive4048题目挂了我也不会出此下策Orz】
初步思路是,设dp[i][j]为当前dp到第i天,股票持有状态为j的情况下的最大获得金额。
那么显然枚举前一天的所有交易决策然后取最大就行了。
关键问题在于状态j如何表示。
首先,最多有8种股票,每种最多可以有8手,所以最先考虑的是用一个8位的9进制数表示状态,不过数字太大数组开不了。
由于股票数总和最多为8,故8位9进制数的所有情况中很多种情况都是不可能出现的。(事实上只有最多12870种情况)
可以想到用map数组来表示dp数组,但这样即使是log级别的查询也会导致TLE。
最后想到的是预处理出0~12869对所有可能出现的状态的映射,这样每次查询就可以做到O(1)。
于是就有了代码中那一段dfs预处理
之后就直接从头到尾DP了,由于要输出最优解的方案所以需要记录路径。
由于避免对不必要计算的状态进行计算,故代码中使用的不是直接DP,而是bfs的方式从最开始的状态展开搜索覆盖最优解。
代码如下,虽然一直改来改去导致其可读性变得非常差。。
将就着看吧Orz
#include<bits/stdc++.h>
#define eps 1e-8
using namespace std;
map<long long,int> st;
const long long nie[9]={1,9,81,729,6561,59049,531441,4782969,43046721};
//预处理
struct vnode
{
int k;
int a[10];
int dnxt[10];
int unxt[10];
};
vnode V[13000];
int top;
int vdfs(long long bitm)
{
if(st.find(bitm)!=st.end())return st[bitm];
st[bitm]=top;
int pos=top;
top++;
long long p=bitm;
V[pos].k=0;
for(int i=0;i<8;i++)
{
V[pos].a[i]=p%9;
V[pos].k+=V[pos].a[i];
p/=9;
}
for(int i=0;i<8;i++)
{
if(V[pos].a[i]>0)V[pos].dnxt[i]=vdfs(bitm-nie[i]);
else V[pos].dnxt[i]=-1;
if(V[pos].k<8&&V[pos].a[i]<8)V[pos].unxt[i]=vdfs(bitm+nie[i]);
else V[pos].unxt[i]=-1;
}
return pos;
}
void init()
{
st.clear();
top=0;
vdfs(0LL);
}
//预处理结束
bool vis[105][13000];
double dp[105][13000];
int lst[105][13000];
double c;
int m,n,k;
int S[10],K[10];
double P[10][105];
char name[10][105];
struct node
{
int i,j;
node(int ii=0,int jj=0):i(ii),j(jj){}
};
void bfs()
{
memset(vis,0,sizeof(vis));
queue<node> q;
q.push(node(0,0));
dp[0][0]=c;
vis[0][0]=true;
while(!q.empty())
{
node p=q.front();
q.pop();
if(p.i==m)continue;
int &b1=p.i,&b2=p.j;
if(V[b2].k<k)
{
for(int i=0;i<n;i++)
{
double w=P[i][b1]*S[i];
if(V[b2].a[i]<K[i]&&(dp[b1][b2]-w>-eps))
{
if(dp[b1+1][V[b2].unxt[i]]<=dp[b1][b2]-w)
{
dp[b1+1][V[b2].unxt[i]]=dp[b1][b2]-w;
lst[b1+1][V[b2].unxt[i]]=b2;
}
if(!vis[b1+1][V[b2].unxt[i]])
{
q.push(node(b1+1,V[b2].unxt[i]));
vis[b1+1][V[b2].unxt[i]]=true;
}
}
}
}
for(int i=0;i<n;i++)
{
double w=P[i][b1]*S[i];
if(V[b2].a[i]>0)
{
if(dp[b1+1][V[b2].dnxt[i]]<=dp[b1][b2]+w)
{
dp[b1+1][V[b2].dnxt[i]]=dp[b1][b2]+w;
lst[b1+1][V[b2].dnxt[i]]=b2;
}
if(!vis[b1+1][V[b2].dnxt[i]])
{
q.push(node(b1+1,V[b2].dnxt[i]));
vis[b1+1][V[b2].dnxt[i]]=true;
}
}
}
if(dp[p.i+1][p.j]<=dp[p.i][p.j])
{
dp[p.i+1][p.j]=dp[p.i][p.j];
lst[p.i+1][p.j]=p.j;
}
if(!vis[p.i+1][p.j])
{
q.push(node(p.i+1,p.j));
vis[p.i+1][p.j]=true;
}
}
}
void print_ans()
{
printf("%.2lf\n",dp[m][0]);
vector<int> ans;
int p=0;
for(int i=m;i>=0;i--)
{
ans.push_back(p);
p=lst[i][p];
}
reverse(ans.begin(),ans.end());
for(int i=1;i<ans.size();i++)
{
int x=ans[i-1],y=ans[i],j;
for(j=0;j<n;j++)
{
if(V[x].unxt[j]==y)
{
printf("BUY %s\n",name[j]);
break;
}
if(V[x].dnxt[j]==y)
{
printf("SELL %s\n",name[j]);
break;
}
}
if(j==n)printf("HOLD\n");
}
}
int main()
{
// freopen("1.txt","r",stdin);
// freopen("2.txt","w",stdout);
init();
bool flag=false;
while(scanf("%lf%d%d%d",&c,&m,&n,&k)==4)
{
if(!flag)flag=true;
else printf("\n");
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++)
{
scanf("%s%d%d",name[i],&S[i],&K[i]);
for(int j=0;j<m;j++)
scanf("%lf",&P[i][j]);
}
bfs();
print_ans();
}
}