题解P1412 经营与开发(DP)

Posted jcnl666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解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;

以上是关于题解P1412 经营与开发(DP)的主要内容,如果未能解决你的问题,请参考以下文章

P1412 经营与开发

洛谷 P1412 经营与开发

HUAS 1477 经营与开发(贪心)

经营与开发(noip2014 模拟题)

noip模拟赛 经营与开发

房地产开发经营与资产管理活动税务筹划思维与方法