NOIP模拟赛 baoj2933数据

来源:互联网 发布:led控制软件下载 编辑:程序博客网 时间:2024/06/10 02:44

题解:

dp很容易看出来,状态转移方程也很容易写

dp[i]=dp[j]+abs(a[j+1]-(i-j-1)) ,然后你就可以发现时间复杂度爆掉了

然后我就发现我不知道该如何优化了,于是写了暴力

然后。。。。并没有拿到说好的60分,而是25分。。。。最后发现在这区区25行的代码中,我的for是从i=2开始的

60分code:

#include<cstdio>#include<cstring>#include<algorithm>const int MAXN=100005;int n,a[MAXN],dp[MAXN];int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++) scanf("%d",&a[i]);    memset(dp,0x3f,sizeof dp); dp[0]=0;    for(int i=1;i<=n;i++)        for(int j=0;j<i;j++)            dp[i]=std::min(dp[i],dp[j]+abs(a[j+1]-(i-j-1)));    printf("%d\n",dp[n]);}
这么短我也能写错。。。

正解:

dp[i]=min(dp[j]+abs(a[j+1]-(i-j-1)))
那么令num[i]=a[i+1]+i+1;
dp[i]=min(dp[j]+abs(num[j]-i));
让我们暂且忽略这个讨厌的abs
dp[i]=min(dp[j]+num[j])-i;
现在,让我们以dp[j]+num[j]为关键字
按照从小到大的关键字搞一个优先队列


那么现在就很有意思了
虽然优先队列搞出来了,但是请注意,abs被忽略掉了
所以现在我们需要把abs的影响也算上


if(num[j]<i) minv=Q.top().key-2*num[j]+i
这样,就可以完美解决abs的问题了


注意,这时的minv仍可能不是最优的
还需要再在num[j]>i的第一个决策点和minv取min


可能细心的人会发现,如果j,k满足num[j]>i&&num[k]<i&&dp[k]+num[k]>dp[j]+num[j]

j为堆顶,但是因为num[j]>i的缘故,k一直在堆的某一个地方,被j堵住,如果此时k这个

决策点比j更优,那岂不是会错?可以证明,如果真的出现上述情况,k并不会比j点更优

有兴趣的可以去翻翻大佬的博客:JK_spc或itselaineZ(证明就是这俩家伙搞出来的)

因为证明有点复杂,不再赘述

代码:

#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<cstring>#include<algorithm>#include<queue>const int MAXN=100005;using namespace std;int minv,n,dp[MAXN],a[MAXN];struct node{    int a,b;    node(){}    node(int t1,int t2){        a=t1; b=t2;    }    bool operator<(const node &x)const{        return a>x.a;    }};priority_queue<node> Q;int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++)    {        scanf("%d",&a[i-1]);        a[i-1]+=i;    }    Q.push(node(a[0],a[0]));    minv=0x3f3f3f3f;//minv不能放在里面,minv的最小值是可能被重复使用的    for(int i=1;i<=n;i++)    {        dp[i]=0x3f3f3f3f;        while(!Q.empty()&&Q.top().b<i)            minv=min(minv,Q.top().a-2*Q.top().b),Q.pop();        dp[i]=min(dp[i],minv+i);        if(!Q.empty()) dp[i]=min(dp[i],Q.top().a-i);        Q.push(node(dp[i]+a[i],a[i]));    }    printf("%d",dp[n]);}


原创粉丝点击