zoukankan      html  css  js  c++  java
  • 【刷题】dp入门总结

     · 笔记 · 

    动态规划求解的问题,一般有两个特征:

    ①最优子结构
    ②重叠子问题

    所以算法的设计思路不在于一下子就想到了某个问题可以使用DP算法,
    而在于先看能不能用穷举法,如果可以用问题可以分解,分治法+穷举可以解决;
    如果问题包含重叠字问题,并且是求解最优解,那么此时用动态规划。

     · tips · 

    【1】精度问题

    【2】重叠子问题

    【3】初始化

    【4】后效性

    · 要点归纳(个人) · 

    【1】卡常问题:

     (1)sqrt能只开一次就只开一次,

    我次次都开,tle 6个点

    (2)精度问题

    不要先开sqrt再加,可能丢失精度,虽然这里问题不大

    (3)不四舍五入 的 输出问题

     最后代码:

    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int n,m;
    const int N=1003;
    int sum[N],st[N][N],f[403][N];
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&sum[i]),sum[i]= sum[i]*sum[i] + sum[i-1];
        
        //memset(f,0x3f,sizeof(f));
        for(int i=1;i<=n;i++)
            f[1][i]=sum[i];
        for(int k=2;k<=m;k++)
            for(int i=k;i<=n;i++)
            {
                f[k][i]=max(f[k-1][k-1],sum[i]-sum[k-1] );
                for(int j=k+1;j<=i;j++)
                    f[k][i]=min(f[k][i],max(f[k-1][j-1],sum[i]-sum[j-1] ));
            }
        int ans=(double)(sqrt((double)f[m][n]))*100;
        //printf("%d.%d",ans/100,ans%100);//错误写法:可能丢了1个前导0,最后没有两位小数
        printf("%.2lf",ans/100.0);
        return 0;
    } 
    View Code

    【2】逢低购买

    先求最长的长度,以减少dp的复杂度

    然后我想的是,记录f[k][i],以min(mx[i],mx[j]+1) * n* n的复杂度dp

    但是!!!

    可以压维成f[i],他的k是mx[i],并且这样是保证找到所有方案的!!!

    直接不tle了

    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int n;
    const int N=5003;
    long long d[N];
    int ans,mx[N];
    
    struct hp
    {
        int len,d[100];
        hp()
        {
            len=1;
            memset(d,0,sizeof(d));
        }
        
        hp operator + (const hp & b ) const
        {
            hp ans;
            ans.len =max(len,b.len );
            int jw=0;
            for(int i=1;i<=ans.len ;i++)
            {
                ans.d[i] =d[i] + b.d[i] +jw ;
                jw=ans.d[i] /10;
                ans.d[i] %=10;
            }
            if(jw) ans.d[++ans.len ]=jw;
            return ans;
        }
    }f[N];
    void print(hp a)
    {
        for(int i=a.len ;i;i--)
            printf("%d",a.d[i] );
    }
    
    long long a[N];
    void get_mx_time()
    {
        int len=0;
        for(int i=1;i<=n;i++)
        {
            int pos=lower_bound(a+1,a+len+1,-d[i])-a;
            a[pos]=-d[i];
            
            if(pos>len) len++;
            mx[i]=pos;
        }
        ans=len;
    }
    
    signed main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%lld",&d[i]);
        get_mx_time();
        printf("%d ",ans);
        
        f[1].len =1,f[1].d[1] =1;
        for(int i=2;i<=n;i++)
        {
            if(mx[i]==1) f[i].len =1,f[i].d[1] =1;
            for(int j=i-1;j;j--)
                if(d[j]>d[i] && mx[i]==mx[j]+1 )
                    f[i]=f[i]+f[j];
                else if(d[j]==d[i] && mx[i]==mx[j] )
                {
                    f[i].len =1,f[i].d[1] =0;
                    break;
                }
        }
        hp tmp;
        for(int i=ans;i<=n;i++)
            if(mx[i]==ans)
                tmp=tmp+f[i];
        print(tmp);
        return 0;
    }
    View Code

    但是UVA的原题需要高精度,虽然我不知道是怎么看出来的,

    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int n;
    const int N=5003;
    int d[N];
    int mx[N];
    
    struct hp
    {
        int len,d[100];
        hp()
        { mem(); }
        void mem()
        {
            memset(d,0,sizeof(d));
            len=1;
        }
        
        hp operator + (const hp b ) const
        {
            hp ans;
            ans.len =max(len ,b.len );
            int jw=0;
            for(int i=1;i<=ans.len ;i++)
            {
                ans.d[i] =d[i] + b.d[i] +jw ;
                jw=ans.d[i] /10;
                ans.d[i] %=10;
            }
            if(jw) ans.d[++ans.len ]=jw;
            return ans;
        }
    }f[N];
    void print(hp a)
    {
        for(int i=a.len ;i>0;i--)
            printf("%d",a.d[i] );
        printf("
    ");
    }
    
    int a[N];
    int get_mx_time()
    {
        int len=0;
        for(int i=1;i<=n;i++)
        {
            int pos=lower_bound(a+1,a+len+1,-d[i])-a;
            a[pos]=-d[i];
            
            if(pos>len) len++;
            mx[i]=pos;
        }
        return len;
    }
    
    signed main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&d[i]);
        int ans=get_mx_time();
        printf("%d ",ans);
        
        for(int i=1;i<=n;i++)
        {
            if(mx[i]==1) f[i].len =1,f[i].d[1] =1;
            for(int j=i-1;j>0;j--)//碰到最右边的一个相等的量,才清空 
                if(d[j]>d[i] && mx[i]==mx[j]+1 )
                    f[i]=f[i]+f[j];
                else if(d[j]==d[i] && mx[i]==mx[j] )
                    f[j].mem() ;
        }
        hp tmp;
        tmp.mem();
        for(int i=ans;i<=n;i++)
            if(mx[i]==ans)
                tmp=tmp+f[i];
        print(tmp);
        return 0;
    }
    View Code

     【3】花店橱窗布置

    很顺手的写出了方程,然后掉了初始化!!!

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int n,m;
    const int N=103,M=103;//花瓶数量,花的数量 
    int g[N][M],f[N][M],pre[N][M];//花瓶对应花 
    
    void get_plan(int i,int j)
    {
        if(j>1)
            get_plan(pre[i][j]-1,j-1);
        printf("%d ",pre[i][j]);
    }
    
    int main()
    {
        scanf("%d%d",&m,&n);
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&g[j][i]);
        
        memset(f,128,sizeof(f));
        //先更新i-1号花瓶装0-i朵花 
        for(int i=1;i<=n;i++)//第i号花瓶 
        {
            f[i][0]=0; 
            for(int j=1;j<=i;j++)
                if(f[i-1][j-1]+g[i][j] > f[i-1][j] )
                    f[i][j]=f[i-1][j-1]+g[i][j],pre[i][j]=i;//我装花的状态从i-1,j-1来,所以我想装花,要i-1,j-1状态为0,
                    //如果不将f[0][0]=0,会导致1号花瓶不能装花 
                else
                    f[i][j]=f[i-1][j],pre[i][j]=pre[i-1][j];
        }
        printf("%d
    ",f[n][m]);
        get_plan(n,m);
        return 0;
    }
    View Code

     【4】迎接仪式

     感谢luogu题解区大佬:

     然后是我的思路:

    (交换型dp get)

    //最后一维表示最后一个字母是什么  
    
    //a[i]=='j'
    //前面是'j',后面可以换成'z'
    //        f[nn-1][i][j-1][0]
    //    不换的时候,f[nn-1][i][j][1] 
    //前面是'z',前后调换
    //        f[nn-1][i-1][j-1][1] +1 
    //    不换的时候,f[nn-1][i][j][1] 
    
    //a[i]=='z'
    //前面是'j',不用换直接加 
    //        f[nn-1][i][j][0] +1 
    //前面是'z',把前面换成'j' 
    //        f[nn-1][i-1][j][1] 
    //    不换的时候,f[nn-1][i][j][1] 
    
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int n,K; 
    const int N=503,M=103;
    char ch[503];
    int d[N],f[N][M][M][2];
    
    int main()
    {
        scanf("%d%d",&n,&K);
        scanf("%s",ch+1);
        for(int i=1;i<=n;i++)
            if(ch[i]=='z') d[i]=1;
        
        memset(f,128,sizeof(f));
        f[0][0][0][1]=0;
        for(int nn=1;nn<=n;nn++)
            for(int i=0;i<=K;i++)//前面通过交换多了几个'j' 
                for(int j=0;j<=K;j++)//多了几个'z' 
                    if(!d[nn] )//'j'
                    {
                        f[nn][i][j][0]=max(f[nn-1][i][j][1],f[nn-1][i][j][0]);
                        if(j>0)
                            f[nn][i][j][1]=max(f[nn-1][i][j-1][0]+1,f[nn-1][i][j-1][1]);
                    }
                    else
                    {
                        f[nn][i][j][1]=max(f[nn-1][i][j][0]+1,f[nn-1][i][j][1]);
                        if(i>0)
                            f[nn][i][j][0]=max(f[nn-1][i-1][j][0],f[nn-1][i-1][j][1]);
                    }
        
        int ans=0;
        for(int i=0;i<=K;i++)
            ans=max(ans,max(f[n][i][i][0],f[n][i][i][1]));
        printf("%d
    ",ans);
        return 0;
    }
    View Code

    【5】环形最大连续子序列

    神奇O(n)解法

    以后碰到可能从中间断开的环,求极值,都可以从从最大最小的差来思考!!!

    【6】最大联通子树和

    #include<cstdio>
    #include<cstdlib>
    #include<queue>
    using namespace std;
    int n,ans1,ans2;
    const int N=5e4+3;
    int d[N],a[N];
    
    int work()//求数组中最大连续子段和 
    {
        int as=-200;
        int pre=0,nw;
        for(int i=1;i<=n;i++)
        {
            nw=a[i];
            if(pre>0) nw+=pre;
            
            pre=nw;
            as=max(as,pre);
        }
        return as;
    }
    void dp1()//环形 最大 连续子段和 的神奇O(n)求法 
    {
        int sum=0;
        for(int i=1;i<=n;i++) a[i]=d[i],sum+=d[i]; 
        ans1=work();
        
        for(int i=1;i<=n;i++) a[i]=-a[i]; 
        ans1=max(ans1,sum+work());
        
        printf("%d
    ",ans1);
    }
    
    int head[N],tot;
    int ev[N<<1],enx[N<<1];
    void add(int u,int v)
    {    ev[++tot]=v,enx[tot]=head[u],head[u]=tot;
        ev[++tot]=u,enx[tot]=head[v],head[v]=tot; }
    
    int f[N];//强制包括rt,和rt的子树的最大联通子树和 
    void dfs(int rt,int fa)
    {
        f[rt]=d[rt];
        for(int i=head[rt];i;i=enx[i])
        {
            if(ev[i]==fa) continue;
            dfs(ev[i],rt);
            
            if(f[ev[i]]>0) f[rt]+=f[ev[i]];
        }
        ans2=max(ans2,f[rt]);
    }
    void dp2()
    {
        int u,v;
        for(int i=1;i<n;i++)
            scanf("%d%d",&u,&v),add(u,v);
        dfs(1,0);
        printf("%d
    ",ans2);
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&d[i]);
        
        dp1();
        dp2(); 
        return 0;
    }
    View Code
  • 相关阅读:
    用JLabel显示时间-- JAVA初学者遇到的一个困难
    linux下观看b站视频,解决字体乱码
    fedora21 codeblocks在编辑装态下无法输入
    fedora21安装无线驱动
    Hdu 1053 Entropy
    linux下的压缩解压命令 tar 的简单描述
    表的截断、删除、创建
    列的维护
    非分区表的重组
    创建高效且易于管理的表以及表的管理和维护
  • 原文地址:https://www.cnblogs.com/xwww666666/p/11761825.html
Copyright © 2011-2022 走看看