一道看似很难的DP
很显然的一个状态就是设到i单位时间的状态
f[i]表示最大收益
g[i]表示最多获得的股票数
G[i]表示在股票数最多的情况下剩余的钱
设m[i]表示第i时间1手股票的价格
O(n^2)就不讲了
f
[
i
]
=
m
a
x
(
g
[
j
]
∗
m
[
i
]
+
G
[
j
]
−
t
a
x
(
g
[
j
]
∗
m
)
)
f[i]=max(g[j]*m[i]+G[j]-tax(g[j]*m))
f[i]=max(g[j]∗m[i]+G[j]−tax(g[j]∗m))
g
[
i
]
=
m
a
x
(
f
[
j
]
)
g[i]=max(f[j])
g[i]=max(f[j]) (能买的股票数)
G
[
i
]
=
m
a
x
(
f
[
j
]
)
−
买
的
股
票
钱
−
税
G[i]=max(f[j])-买的股票钱-税
G[i]=max(f[j])−买的股票钱−税(即剩余钱数)
其中tax(n)表示买/卖价钱为n的税价
(如果买了能更优的话肯定多买)
考虑优化。
显然max(f[j])可以直接维护,问题就是g和G的维护。
事实上可以维护最大的g,在g最大的情况下维护最大的G。
但是这样设有个问题,比如有j和k两个位置,
其中j能买的股票比k多,但是k剩余的钱比j多。
这就需要证明。
正确性证明:
因为m[j]不可能>=m[i](会亏本傻是可以的但是要有个限度)
所以m[j]< m[i]
先不考虑税,如果有两个状态j,k,设j能买的股票为gj,剩余Gj元,k同理。
显然1<=m[j],m[k]< m[i]
设m表示当前价格(即m[i])
如果j比k优,那么显然要满足
g
j
∗
m
+
G
j
>
g
k
∗
m
+
G
k
gj*m+Gj>gk*m+Gk
gj∗m+Gj>gk∗m+Gk
化简得到
m
>
(
G
k
−
G
j
)
/
(
g
j
−
g
k
)
m>(Gk-Gj)/(gj-gk)
m>(Gk−Gj)/(gj−gk)
因为Gj、Gk都是m[j]、m[k]的余数(能买肯定多买),所以
G
j
,
G
k
<
m
[
j
]
,
m
[
k
]
<
m
Gj,Gk<m[j],m[k]< m
Gj,Gk<m[j],m[k]<m
G
k
−
G
j
<
m
Gk-Gj<m
Gk−Gj<m
所以只要
(
g
j
−
g
k
)
>
=
1
(gj-gk)>=1
(gj−gk)>=1式子就能成立。
因为gj、gk都是整数,所以只要gj> gk就一定更优。
所以只要维护最大的g,在g最大的情况下维护最大的G。
加税后显然。
那么还有一个问题,那就是g的求法。
个人最先想到的就是假设不加税的情况下求出g,然后如果不成立就g–直到成立。
然而这样复杂度玄妙
g2=floor(f/m);
while ((g2*m+tax(g2*m))>f)
g2--;
G2=f-(g2*m+tax(g2*m));
大概就是这样(小于等于是因为最终的目的是让g2合法 --10/16/2018)
(
g
2
∗
m
+
t
a
x
(
g
2
∗
m
)
)
<
=
f
(g2*m+tax(g2*m))<=f
(g2∗m+tax(g2∗m))<=f
然后发现把tax()拆开后变成这样
(
g
2
∗
m
+
g
2
∗
m
∗
t
+
m
a
x
(
g
2
∗
m
∗
s
1
,
s
2
)
)
<
=
f
(g2*m+g2*m*t+max(g2*m*s1,s2))<=f
(g2∗m+g2∗m∗t+max(g2∗m∗s1,s2))<=f
那么max中有两种情况
①
m
a
x
=
g
2
∗
m
∗
s
1
max=g2*m*s1
max=g2∗m∗s1
则
(
g
2
∗
m
+
g
2
∗
m
∗
t
+
g
2
∗
m
∗
s
1
)
<
=
f
(g2*m+g2*m*t+g2*m*s1)<=f
(g2∗m+g2∗m∗t+g2∗m∗s1)<=f
g
2
∗
m
∗
(
1
+
t
+
s
1
)
<
=
f
g2*m*(1+t+s1)<=f
g2∗m∗(1+t+s1)<=f
g
2
<
=
f
/
m
/
(
1
+
t
+
s
1
)
g2<=f/m/(1+t+s1)
g2<=f/m/(1+t+s1)
显然可以直接算。
②
m
a
x
=
s
2
max=s2
max=s2
则
(
g
2
∗
m
+
g
2
∗
m
∗
t
+
s
2
)
<
=
f
(g2*m+g2*m*t+s2)<=f
(g2∗m+g2∗m∗t+s2)<=f
g
2
∗
m
∗
(
1
+
t
)
<
=
f
−
s
2
g2*m*(1+t)<=f-s2
g2∗m∗(1+t)<=f−s2
g
2
<
=
(
f
−
s
2
)
/
m
/
(
1
+
t
)
g2<=(f-s2)/m/(1+t)
g2<=(f−s2)/m/(1+t)
直接算+1
因为是分类讨论,所以最后还要分别判断两种情况是否合法,之后再分别更新,这样才是最严谨的做法
(16/10/2018)
code
#include <iostream>
#include <cstdio>
#include <cmath>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(x,y) (x<y?x:y)
#define max(x,y) (x>y?x:y)
using namespace std;
int T,i,j,k,l,n,g,g2;
double s1,s2,t,mf,f,G,G2,m;
double tax(double n){return (n*t+max(n*s1,s2));}
void change()
{
G2=f-(g2*m+tax(g2*m));
if (g2>g)
{
g=g2;
G=G2;
}
else
if ((g2==g) && (G2>G))
G=G2;
}
int main()
{
freopen("stock.in","r",stdin);
freopen("stock.out","w",stdout);
scanf("%d",&T);
for (;T;T--)
{
scanf("%lf%lf%lf%lf",&f,&s1,&s2,&t);
scanf("%d",&n);
g=0;
G=0;
fo(i,1,n)
{
scanf("%lf",&m);
m*=100;
f=max(f,g*m+G-tax(g*m));
g2=f/m/(t+1+s1);
if ((g2*m+tax(g2*m))<=f && g2*m*s1>=s2) change();
g2=(f-s2)/m/(t+1);
if ((g2*m+tax(g2*m))<=f && s2>=g2*m*s1) change();
}
printf("%0.3lf\n",f);
}
fclose(stdin);
fclose(stdout);
return 0;
}
以下是旧代码
#include <iostream>
#include <cstdio>
#include <cmath>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(x,y) (x<y?x:y)
#define max(x,y) (x>y?x:y)
using namespace std;
int T,i,j,k,l,n,g,g2;
double s1,s2,t,mf,f,G,G2,m;
double tax(double n){return (n*t+max(n*s1,s2));}
int main()
{
freopen("stock.in","r",stdin);
freopen("stock.out","w",stdout);
scanf("%d",&T);
for (;T;T--)
{
scanf("%lf%lf%lf%lf",&f,&s1,&s2,&t);
scanf("%d",&n);
g=0;
G=0;
fo(i,1,n)
{
scanf("%lf",&m);
m*=100;
f=max(f,g*m+G-tax(g*m));
g2=f/m/(t+1+s1);
if ((g2*m+tax(g2*m))>f)
g2=(f-s2)/m/(t+1);
G2=f-(g2*m+tax(g2*m));
if (g2>g)
{
g=g2;
G=G2;
}
else
if ((g2==g) && (G2>G))
G=G2;
}
printf("%0.3lf\n",f);
}
fclose(stdin);
fclose(stdout);
return 0;
}
更新:16/10/2018 18:14