zoukankan      html  css  js  c++  java
  • DP优化

    学了好几个月DP就是没学优化,现在终于开始啃这块知识了...

    很典型的DP题,显然我们可以设f[x]表示上一次跳到x的最大能量...

    40分暴力:

    #include<bits/stdc++.h>
    #define max(a,b) (((a)>(b))?(a):(b))
    using namespace std;
    const int N=2001000;
    int f[N],n,k,sum[N];
    inline int read()
    {
        int x=0,ff=1;
        char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        return x*ff;
    }
    int main()
    {
        freopen("1.in","r",stdin);
        n=read();k=read();
        for(register int i=1;i<=n;++i) sum[i]=sum[i-1]+read();
        f[0]=k;
        for(register int i=1;i<=n;++i)
            for(register int j=0;j<i;++j) if(f[j]>=100*i) f[i]=max(f[i],f[j]+sum[i]-sum[j]-100*i);
        printf("%d
    ",f[n]);
        return 0;    
    }

    那我们接下来就要考虑优化了,

    f[i]=max(f[i],f[j]+sum[i]-sum[j]-i*100);
    f[i]=max(f[j]-sum[j])+sum[i]-i*100;
    保证f[j]-sum[j]最优...
    思考对于i的决策x和y,假定x<y且都合法
    那么如果f[x]-sum[x]>=f[y]-sum[y],我们这里只需在证明f[y]>f[x]即可证明决策具有单调性...
    也就是说随着最优决策递增,答案也在递增...
    将上式变形:f[x]-f[y]>=sum[x]-sum[y],这里显然sum[x]-sum[y]>0,所以f[x]-f[y]>0;
    证毕.

    #include<bits/stdc++.h>
    #define max(a,b) (((a)>(b))?(a):(b))
    using namespace std;
    const int N=2001000;
    int f[N],n,k,sum[N],q[N],head,tail;
    inline int read()
    {
        int x=0,ff=1;
        char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        return x*ff;
    }
    int main()
    {
        freopen("1.in","r",stdin);
        n=read();k=read();
        for(register int i=1;i<=n;++i) sum[i]=sum[i-1]+read();
        f[0]=k;
        for(register int i=1;i<=n;++i)
        {
            while(head<=tail&&f[q[head]]<100*i) ++head;
            f[i]=f[q[head]]-sum[q[head]]+sum[i]-100*i;
            while(head<=tail&&f[i]-sum[i]>=f[q[tail]]-sum[q[tail]]) --tail;
            q[++tail]=i;
        }
        printf("%d
    ",f[n]);
        return 0;    
    }

     

    这个题...

    我们很显然要设f[i][j]表示前i个点且最后一个点高度为j时的最小状态,但高度太大了

    之后我们发现修改后的高度只会与原高度相同,所以将数据离散...

    暴力由此产生:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2100;
    int n,h[N],f[N][N],ans=1e9,ff[N][N];//f[i][j]表示前i个数且最后一个数高度为h[j]时的最小代价. 
    vector<int>v1[N],v2[N];
    inline int read()
    {
        int x=0,ff=1;
        char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        return x*ff;
    }
    int main()
    {
    //    freopen("1.in","r",stdin);
        n=read();
        for(register int i=1;i<=n;++i) h[i]=read();
        memset(f,127,sizeof(f));memset(ff,127,sizeof(ff));
        for(register int i=1;i<=n;++i)
            for(register int j=1;j<=i;++j)
            {
                if(h[j]<=h[i]) v1[i].push_back(j);
                if(h[j]>=h[i]) v2[i].push_back(j);
            }
        for(register int i=1;i<=n;++i) ff[1][i]=f[1][i]=abs(h[1]-h[i]);
        for(register int i=2;i<=n;++i)
            for(register int j=1;j<=n;++j)
            {
                for(register int k=0;k<v1[j].size();++k) f[i][j]=min(f[i][j],f[i-1][v1[j][k]]+abs(h[i]-h[j]));
                for(register int k=0;k<v2[j].size();++k) ff[i][j]=min(ff[i][j],ff[i-1][v2[j][k]]+abs(h[i]-h[j]));
            }
        for(register int i=1;i<=n;++i) ans=min(ans,min(f[n][i],ff[n][i]));        
        printf("%d",ans);
        return 0;
    } 

    O(n3)肯定吃不消,考虑优化;

    我们可以将h[i]排序,

    for(register int i=2;i<=n;++i)
            for(register int j=1;j<=n;++j)
                for(register int k=1;k<=j;++k)
                    f[i][j]=min(f[i][j],f[i-1][k]+abs(h[i]-t[j]));

    代码就变成这样了,我们发现在k的枚举中abs(h[i]-t[j])与k无关,有关的只有f[i-1][k](k<=j),而这个就是我们优化的重点,我们可以用o[i][j]表示在阶段i,j时的最优决策;

    那么只用在i时更新下一阶段的最优决策就可以省掉一个循环

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2100;
    int n,h[N],f[N][N],ans=1e9,t[N],o[N][N];//f[i][j]表示前i个数且最后一个数高度为h[j]时的最小代价. 
    inline int read()
    {
        int x=0,ff=1;
        char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        return x*ff;
    }
    int main()
    {
        freopen("1.in","r",stdin);
        n=read();
        for(register int i=1;i<=n;++i) h[i]=read(),t[i]=h[i];
        sort(t+1,t+1+n);
        memset(f,127,sizeof(f));
        memset(o,127,sizeof(o));
        for(register int i=1;i<=n;++i) 
        {
            f[1][i]=abs(h[1]-t[i]);
            o[1][i]=min(o[1][i-1],f[1][i]);
        }
        for(register int i=2;i<=n;++i)
        {
            for(register int j=1;j<=n;++j) 
            {
                f[i][j]=o[i-1][j]+abs(h[i]-t[j]);
                o[i][j]=min(o[i][j-1],f[i][j]);
            }
        }
        for(register int i=1;i<=n;++i) ans=min(ans,f[n][i]);        
        printf("%d",ans);
        return 0;
    } 

    然而,这道题还有更优秀的贪心...

    我们考虑一个递增序列,之后又来了一个小的数字;

    设序列中的最大值为a,次大值为b,新来的数字为c,c<a,这时我们要花费代价至少(c-a);

    考虑这些代价可以做出那些改变,可以将c与a两个数字改成(c,c),(c+1,c+1)...(a-1,a-1),(a,a);

    而此时b一定在这些数里,我们要保证不下降,且尽量使最大的值尽可能小,只能将a和c都改成b;

    而这用大根堆很容易维护,就不写代码了...

  • 相关阅读:
    分享完整的项目工程目录结构
    2014年糯米网校
    高并发非自增ID如何设计?
    Asp.Net中使用Couchbase——Memcached缓存使用篇
    协作图(通信图)collaboration diagram
    解决java获取系统时间差8个小时 专题
    智言趣语
    Common class for judge IPV6 or IPV4
    判断参数是否符合要求的一个类
    Connection to https://dl-ssl.google.com refused的解决办法
  • 原文地址:https://www.cnblogs.com/gcfer/p/11611335.html
Copyright © 2011-2022 走看看