【题解】P1412 经营与开发(DP)

Description

给出 \(n\) 个星球,每个星球有一个类型,如果该星球 \(i\) 类型是 \(1\) 则我们可以在它上面挖钻,可以得到 \(a[i] \times p\) 的价值(p是我们的钻头的属性),每次用完之后属性会变成 \(p \times (1-0.01 \times k\)。 如果类型是 \(2\),我们可以在上面修理钻头,花费 \(b[i]*p\) 的价格可以让我们的钻头属性变成 \(p \times (1+0.01 \times c)\)。输出最大可以获得的价值。可以透支价值(假装有信用卡)

Sample Input

5 50 50 10

1 10

1 20

2 10

2 20

1 30

Sample Output

375.00

Solution

我们发现这题如果我们从 \(1\) 推到 \(n\) 那么的话我们发现其实钻头的值会对后面的答案有影响。

那么怎么办?

我们可以考虑一个可以计算出钻头属性的状态。

灵光一动: \(f[i][j][k]\) 表示前 \(i\) 个,选了 \(j\) 个星球挖矿, \(k\) 个星球修钻头可以获得的最大价值。

那么转移就很简单了,同时我们还可以用滚动数组优化空间。(虽然优化了还是过不了。。。)

时间复杂度 \(O(N^3)\)

空间复杂度 \(O(N^2)\)

贴个代码:

#include<bits/stdc++.h>
using namespace std;

int w,k,c;
const int N=1005,INF=1E9;
int type[N],a[N],sum[N];
double f[2][N][N];

inline double power(double a,int b){
    double ret=1.0;
    while(b){
        if(b&1) ret*=a;
        a*=a;
        b>>=1;
    }
    return ret;
}

int main(){
    int n=0;
    scanf("%d%d%d%d",&n,&k,&c,&w);
    for(int i=1;i<=n;++i) 
        scanf("%d%d",&type[i],&a[i]),sum[i]=sum[i-1]+(type[i]&1);
    memset(f,-60,sizeof(f));
    f[0][0][0]=0;
    double ans=-INF;
    double p1=1-0.01*(double)k,p2=1+0.01*(double)c;
    for(int i=1;i<=n;++i){
        int now=i%2,pre=(i-1)%2;
        memset(f[now],-60,sizeof(f[now]));
        for(int j=0;j<=sum[i];++j) 
            for(int k=0;k<=i-sum[i];++k){
                f[now][j][k]=f[pre][j][k];
                if(type[i]==1 && j>=1)
                    f[now][j][k]=max(f[now][j][k],f[pre][j-1][k]+(double)a[i]*power(p1,j-1)*power(p2,k));
                if(type[i]==2 && k>=1)
                    f[now][j][k]=max(f[now][j][k],f[pre][j][k-1]-(double)a[i]*power(p1,j)*power(p2,k-1));
                ans=max(ans,f[now][j][k]);
            }
        }
    printf("%.2lf\n",ans*(double)w);
    return 0;
}

那么我们怎么快乐的A掉这道题捏?

我们来先写个式子:

我们设 \(k_i\)\((1-0.01 \times k)\) 或者 \((1+0.01 \times c)\)

\(a[1]*w+a[2]*w*k_1+a[3]*w*k_1*k_2+a[4]*w*k_1*k_2*k_3...\)

那么我们先提出:

\(w*[a[1]+a[2]*k_1+a[3]*k_1*k_2+a[4]*k_1*k_2*k_3...]\)

那么我们可以直接用秦某韶的公式

\(w*[a[1]+k_1*(a[2]+k_2*(a[3]+a[4]*k_3...))]\)

那么我们发现:
!!!怎么越到最后值越固定,并且好像从里到外求值好像不具有后效性。

我们想到了一个绝妙的方法只可惜这里。。。

算了,我们发现可以直接从里到外求值并且只要保证里面的局部最优就可以了。

那么我们从后到前枚举并且保证有后面局部最优。

那么枚举一下就可以了。

#include<bits/stdc++.h>
using namespace std;

int w,k,c;
double ans;
const int N=1e5+5,INF=1E9;
int type[N],a[N];

int main(){
    int n=0;
    scanf("%d%d%d%d",&n,&k,&c,&w);
    for(int i=1;i<=n;++i) 
        scanf("%d%d",&type[i],&a[i]);
    ans=0.0;
    for(int i=n;i>=1;--i)
        if(type[i]==1) ans=max(ans,ans*(1.0-0.01*(double)k)+(double)a[i]);
        else ans=max(ans,ans*(1.0+0.01*(double)c)-(double)a[i]);
    printf("%.2lf\n",ans*(double)w);
    return 0;
}
posted @ 2019-06-24 22:18  章鱼那个哥  阅读(194)  评论(0编辑  收藏  举报