UVALive 4048 Fund Management(状压DP)

这篇博客介绍了如何解决UVALive 4048题目——Fund Management,该问题涉及股票交易的动态规划策略。作者提出用一个8位9进制数表示股票持有状态,但由于状态数量巨大,尝试使用map导致超时。最终,通过预处理映射状态并采用广度优先搜索(BFS)避免不必要的计算,实现了O(1)查询的解决方案。代码虽然可读性不高,但展示了问题解决的思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:点击打开链接

【链接为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();
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值