P1412 经营与开发
题目描述
4X概念体系,是指在PC战略游戏中一种相当普及和成熟的系统概念,得名自4个同样以“EX”为开头的英语单词。
eXplore(探索)
eXpand(拓张与发展)
eXploit(经营与开发)
eXterminate(征服)
——维基百科
今次我们着重考虑exploit部分,并将其模型简化:
你驾驶着一台带有钻头(初始能力值w)的飞船,按既定路线依次飞过n个星球。
星球笼统的分为2类:资源型和维修型。(p为钻头当前能力值)
1.资源型:含矿物质量a[i],若选择开采,则得到a[i]*p的金钱,之后钻头损耗k%,即p=p*(1-0.01k)
2.维修型:维护费用b[i],若选择维修,则支付b[i]*p的金钱,之后钻头修复c%,即p=p*(1+0.01c)
注:维修后钻头的能力值可以超过初始值(你可以认为是翻修+升级)
金钱可以透支。
请作为舰长的你仔细抉择以最大化收入。
输入输出格式
输入格式:
第一行4个整数n,k,c,w。
以下n行,每行2个整数type,x。
type为1则代表其为资源型星球,x为其矿物质含量a[i];
type为2则代表其为维修型星球,x为其维护费用b[i];
输出格式:
一个实数(保留2位小数),表示最大的收入。
输入输出样例
说明
【数据范围】
对于30%的数据 n<=100
另有20%的数据 n<=1000;k=100
对于100%的数据 n<=100000; 0<=k,c,w,a[i],b[i]<=100;保证答案不超过10^9
先上30分的代码(三维数组空间开不了那么大)
三维
f(i,j,k)代表前i个,开采j个,维护k个的最大价值
设p1,p2为那两个固定系数
t=1
f(i,j,k)=max(f(i-1,j,k),f(i-1,j-1,k)+p1^(j-1)*p2^k*a[i])
t=2
f(i,j,k)=max(f(i-1,j,k),f(i-1,j,k-1)+p1^j*p2^(k-1)*a[i])
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define inf 2147483647 const ll INF = 0x3f3f3f3f3f3f3f3fll; #define ri register int template <class T> inline T min(T a, T b, T c) { return min(min(a, b), c); } template <class T> inline T max(T a, T b, T c) { return max(max(a, b), c); } template <class T> inline T min(T a, T b, T c, T d) { return min(min(a, b), min(c, d)); } template <class T> inline T max(T a, T b, T c, T d) { return max(max(a, b), max(c, d)); } #define pi acos(-1) #define me(x, y) memset(x, y, sizeof(x)); #define For(i, a, b) for (int i = a; i <= b; i++) #define FFor(i, a, b) for (int i = a; i >= b; i--) #define mp make_pair #define pb push_back const int maxn = 100005; #define mod 100003 const int N=200; // name******************************* double f[N][N][N]; double p1,p2; int n; double k,c,w; int t[N]; double a[N],b[N]; double ans=0; int s1[N]; int s2[N]; // function****************************** double qmul(double a,int b) { double base=a; double ans=1; while(b) { if(b&1) ans*=base; base*=base; b>>=1; } return ans; } //*************************************** int main() { // freopen("test.txt","r",stdin); cin>>n>>k>>c>>w; For(i,1,n) { s1[i]=s1[i-1]; s2[i]=s2[i-1]; cin>>t[i]; if(t[i]==1) { cin>>a[i]; s1[i]++; } else { cin>>b[i]; s2[i]++; } } p1=1-0.01*k; p2=1+0.01*c; fill(&f[0][0][0],&f[N-1][N-1][N-1],-inf); f[0][0][0]=0; For(i,1,n) { For(j,0,s1[i]) { For(k,0,s2[i]) { f[i][j][k]=f[i-1][j][k]; if(t[i]==1&&j>=1) f[i][j][k]=max(f[i][j][k],f[i-1][j-1][k]+qmul(p1,j-1)*qmul(p2,k)*a[i]); if(t[i]==2&&k>=1) f[i][j][k]=max(f[i][j][k],f[i-1][j][k-1]-qmul(p1,j)*qmul(p2,k-1)*b[i]); ans=max(ans,f[i][j][k]); // cout<<i<<","<<j<<","<<k<<":"<<f[i][j][k]<<endl; } } } printf("%.2f",ans*w); return 0; }
再看一维神奇代码
逆着走,就能压成一维了!!!
记最终答案为ans,其实对于每个星球不管是维修还是资源型,都可以把对钻头的改变度记作一个常数k,而且k是有后效性的。
我们先具一个i=4的例子,
ans=w*k1*a1+w*k1*k2*a2+w*k1*k2*k3*a3+w*k1*k2*k3*k4*a4,
即ans=w*(k1*a1+k1*k2*a2+k1*k2*k3*a3+k1*k2*k3*a4),
进一步提公因式就是ans=w*(k1*(a[1]+k2*(a[2]+k3*(a[3]+k4*a[4]))))
所以我们发现从最里面往外面走是最好的,也就是逆着推,真特么神奇
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define inf 2147483647 const ll INF = 0x3f3f3f3f3f3f3f3fll; #define ri register int template <class T> inline T min(T a, T b, T c) { return min(min(a, b), c); } template <class T> inline T max(T a, T b, T c) { return max(max(a, b), c); } template <class T> inline T min(T a, T b, T c, T d) { return min(min(a, b), min(c, d)); } template <class T> inline T max(T a, T b, T c, T d) { return max(max(a, b), max(c, d)); } #define pi acos(-1) #define me(x, y) memset(x, y, sizeof(x)); #define For(i, a, b) for (int i = a; i <= b; i++) #define FFor(i, a, b) for (int i = a; i >= b; i--) #define mp make_pair #define pb push_back const int maxn = 100005; #define mod 100003 const int N=100005; // name******************************* int n; double f[N]; double k,c,w; int t[N]; double a[N],b[N]; double ans=-inf; // function****************************** //*************************************** int main() { // freopen("test.txt","r",stdin); scanf("%d%lf%lf%lf",&n,&k,&c,&w); For(i,1,n) { scanf("%d",&t[i]); if(t[i]==1) scanf("%lf",&a[i]); else scanf("%lf",&b[i]); } double p1=1-0.01*k; double p2=1+0.01*c; For(i,0,n) f[i]=-inf; f[n+1]=0; FFor(i,n,1) { if(t[i]==1) f[i]=max(f[i+1],f[i+1]*p1+a[i]); else f[i]=max(f[i+1],f[i+1]*p2-b[i]); } printf("%.2f",f[1]*w); return 0; }