• DP套题练习3


    T1:火车站内往往设有一些主干线分叉出去的铁路支路,供火车停靠,以便上下客或装载货物铁路支路有一定长度;火车也有一定的长度,且每列火车的长度相等.假设某东西向的铁路上,有一小站.该站只有一条铁路支路可供火车停靠,并且该铁路支路最多能容纳 M 辆火车为了火车行驶的通畅,该站只允许火车自东方进站,自西方出站,且先进站的火车必须先出站,否则,站内火车将发生堵塞该火车站工作任务繁忙.每天都有 N 辆自东方驶向西方的火车要求在预定时刻进站,并在站内作一定时间的停靠.为了满足每辆进站火车的要求,小站的调度工作是井井有条地开展.在小站每天的工作开始前,小站工作人员须阅读所有火车的进站申请,并决定究竞接受哪些火车的申请.而对于不能满足要求的火车,小站必须提前通知它们,请它们改变行车路线,以免影响正常的铁路运输工作.由于火车进站、出站的用时可以忽略不计,小站允许几辆火车同时进站或出站,且小站工作人员可以任意安排这些火车进站的先后排列次序.小站的工作原则是尽量地满足申请火车的要求请你编一个程序,帮助工作人员考察某天所有火车的进站申请,计算最多能满足多少火车的要求.

    S1:显然当m=1时就是贪心决策的"活动安排".要总结一下对于区间问题,不要随意用[ 0/1 ]表示选不选,可能是本人太菜了,这样推只成功过一次.回归本题,我们按进站时间sort(如果进站时间相同则按出站时间排序),再分m=1/2/3来讨论.当m=1时,可以贪心,也可dp.我们设f [ i ]表以第 i 辆车开头能合法最长车辆个数,显然f[ i ]=max{f[ j ]+1}(len[ j ].L>len[ i ].R),注意初始化f[ i ]=1.当m = 2,我们用f[ i ][ j ]表示以第 i辆为第 1 辆车,第 j 辆为第 2 辆车为开头能合法最长的车辆个数,f[ i ][ j ]=max{f[ j ][ k ]+1},这时注意i,j,k要保证i,j,k先进先出且k要等i出站再进站.当m = 3,f[ i ][ j ][ k ]=max{f[  j ][ k ][ g ]+1;}注意i, j,k先进先出,j,k,g先进先出,且g在i出站后进站.以集合结尾几个元素代表集合可以联系练习T3.

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    #define re register
    int n,m,f1[101],f2[101][101],f3[101][101][101];
    struct bian{
        int L,R;
    }len[110];
    bool cmp(bian a,bian b){
        if(a.L==b.L)
            return a.R<=b.R;
        else return a.L<b.L;
    }
    void work1()
    {
        int ans=0;
        for(re int i=n;i>=1;--i)
            for(re int j=i+1;j<=n;++j)
                if(len[j].L>=len[i].R){
                    if(!f1[j]) f1[j]=1;
                    f1[i]=max(f1[i],f1[j]+1);
                    ans=max(f1[i],ans);    
                }
        printf("%d",ans);
    }
    void work2()
    {
        int ans=0;
        for(re int i=n;i>=1;--i)
            for(re int j=i+1;j<=n;++j){
                if(len[j].R<len[i].R) continue;
                if(!f2[i][j]) f2[i][j]=2;
                for(re int k=j+1;k<=n;++k){
                    if(len[k].R<len[j].R||len[k].L<len[i].R) continue;
                    if(!f2[j][k]) f2[j][k]=2;
                    f2[i][j]=max(f2[i][j],f2[j][k]+1);
                    ans=max(ans,f2[i][j]);
                }
            }
        printf("%d",ans);
    }
    void work3()
    {
        int ans=0;
        for(re int i=n;i>=1;--i)
            for(re int j=i+1;j<=n;++j)
                for(re int k=j+1;k<=n;++k){
                    if(len[j].R<len[i].R||len[k].R<len[j].R)
                        continue;
                    if(!f3[i][j][k]) f3[i][j][k]=3;
                    for(re int g=k+1;g<=n;++g){
                        if(len[g].R<len[k].R||len[g].L<len[i].R) continue;
                        if(!f3[j][k][g]) f3[j][k][g]=3;
                        f3[i][j][k]=max(f3[i][j][k],f3[j][k][g]+1);
                        ans=max(f3[i][j][k],ans); 
                    }
                }
        printf("%d",ans);
    }
    int main()
    {
        freopen("train.in","r",stdin);
        freopen("train.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(re int i=1;i<=n;++i)
            scanf("%d%d",&len[i].L,&len[i].R);
        sort(len+1,len+1+n,cmp);
        if(m==1){work1();return 0;}
        else if(m==2){work2();return 0;}
        else {work3();return 0;}
        return 0;
    }

    T2:给定一具有 N 个顶点(从 1 到 N 编号)的凸多边形,每个顶点的权均已知。问如何把这个凸多边形划分成 N-2 个互不相交的三角形,使得这些三角形顶点的权的乘积之和最小.

    S2:显然的区间dp,由小区间的最值推大区间的最值.但有一点麻烦的是如果我们设f[ i ][ j ]代表区间[ i , j ]点最成多边形的乘积之和最小,在枚举分割点k时我们发现无法用f[ i ][ j ]去表示不是连续点组成的多边形(如①③④组成的三角形),自己还想用map求判断每种多边形,显然这样n!数量要爆long long的.看了题解方程f[ i ][ j ]=min(f[ i ][ k ]+f[ k ][ j ]+val[ i ]*val[ j ]*val[ k ]).发现这样转移能巧妙地将无法表示的多边形直接计算出来,差了转换的思想.

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    #define e exit(0)
    #define re register
    #define LL long long
    long long n,val[110],f[110][110];
    long long getv(LL x,LL y,LL z){return val[x]*val[y]*val[z];}
    int main()
    {
        freopen("division.in","r",stdin);
        freopen("division.out","w",stdout);
        scanf("%lld",&n);
        for(re LL i=1;i<=n;++i)
            scanf("%lld",&val[i]);
        memset(f,0x7f,sizeof(f));
        for(re LL len=2;len<=n;++len)
            for(re LL i=1;i+len-1<=n;++i){
                LL j=i+len-1;
                if(j==i+1) f[i][j]=0;
                for(re LL k=i+1;k<=j-1;++k)
                    f[i][j]=min(f[i][j],f[i][k]+f[k][j]+getv(i,k,j));
            }
        printf("%lld",f[1][n]);
        return 0;
    }

    T3:加分二叉树

    S3:我们会发现对于区间[ i , j ],根结点k是决定同一中序不同形态树的关键.我们设f[ i ][ j ]表示区间[ i, j ]最高加分.我们枚举根节点k,f[ i ][  j ]=max{f[ i ][ k - 1]+f[ k + 1 ][ j ] + val[ k ]},注意k要讨论i,j两个端点情况.再用root数组记录转移关键点,记录区间根节点.我们不断递归区间根据根节点建树,在进行前序遍历即可.

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    #define e exit(0)
    #define re register
    #define LL long long
    long long n,s,val[110],vis[110],f[110][110],root[110][110];
    struct shu{
        LL x,ls,rs;
    }tree[110*2];
    void add(LL fa,LL son,LL flag)
    {
        tree[fa].x=fa,tree[son].x=son;
        if(!flag)
            tree[fa].ls=son;
        else if(flag)
            tree[fa].rs=son;
    }
    LL build(LL l,LL r)
    {
        LL s=root[l][r];
        if(l==r){
            tree[l].ls=tree[l].rs=0;
            tree[l].x=l;
            return l;
        }
        else if(s==l){
            LL rs=build(s+1,r);
            add(s,rs,1);
            tree[s].ls=0;
            return s;
        }
        else if(s==r){
            LL ls=build(l,s-1);
            add(s,ls,0);
            tree[s].rs=0;
            return s;
        }
        else{
            LL ls=build(l,s-1);
            LL rs=build(s+1,r);
            add(s,ls,0),add(s,rs,1);
            return s;
        }
    }
    void DLR(LL x)
    {
        printf("%lld ",x);
        if(tree[x].ls)
            DLR(tree[x].ls);
        if(tree[x].rs)
            DLR(tree[x].rs);
    }
    int main()
    {
        freopen("tree.in","r",stdin);
        freopen("tree.out","w",stdout);
        scanf("%lld",&n);
        for(re LL i=1;i<=n;++i){
            scanf("%lld",&val[i]);
            f[i][i]=val[i];
        }
        for(re LL len=2;len<=n;++len)
            for(re LL i=1;i+len-1<=n;++i){
                LL j=i+len-1;
                f[i][j]=max(f[i][j],f[i+1][j]+val[i]);
                f[i][j]=max(f[i][j],f[i][j-1]+val[j]);
                for(re LL k=i+1;k<=j-1;++k)
                    f[i][j]=max(f[i][j],f[i][k-1]*f[k+1][j]+val[k]);
            }
        for(re LL len=2;len<=n;++len)
            for(re LL i=1;i+len-1<=n;++i){
                LL j=i+len-1;
                if(f[i][j]==f[i+1][j]+val[i])
                    root[i][j]=i;
                else if(f[i][j]==f[i][j-1]+val[j])
                    root[i][j]=j;
                else for(re LL k=i+1;k<=j-1;++k)
                    if(f[i][j]==f[i][k-1]*f[k+1][j]+val[k]){
                        root[i][j]=k;
                        break;
                    }
            }
        printf("%lld
    ",f[1][n]);
        s=build(1,n);
        DLR(s);
        return 0;
    }

    T4:最长前缀

    S4:DP水题.f[ i ]表示能不能用字符串组成前 i 个字符.这样我们对每个 长度i往前推已知的字符长度len,如果f[ i - len ]合法则继承更新.

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    #define e exit(0)
    #define R register
    string s[110];
    char t[500010];
    int n,lent,flag,lens[110],f[500010];
    int main()
    {
        freopen("prefix.in","r",stdin);
        freopen("prefix.out","w",stdout);
        scanf("%d",&n);
        for(R int i=1;i<=n;++i){
            scanf("%d",&lens[i]);
            cin>>s[i];
        }
        scanf("%s",t+1);
        lent=strlen(t+1);
        --lent;
        f[0]=1;
        for(R int i=1;i<=lent;++i){
            for(R int j=1;j<=n;++j){
                int len=lens[j];
                int L=i-len,sur=0;
                flag=1;
                if(L<0||f[L]==0) continue;
                ++L;
                for(R int k=L;k<=i;++k)
                {
                    if(s[j][sur]!=t[k]){
                        flag=0;
                        break;
                    }
                    ++sur;
                }
                if(flag){
                    f[i]=1;
                    break;
                }
            }
        }
        for(R int i=lent;i>=1;--i)
            if(f[i]){
                printf("%d",i);
                return 0;
            }
        printf("0");
        return 0;
    }
  • 相关阅读:
    设置nginx中文件上传的大小限制度
    百度编辑器(ueditor)踩坑,图片转存无法使用
    帝国cms更换Ueditor编辑器上传图片加水印
    帝国cms7.5整合百度编辑器ueditor教程
    帝国CMS万能标签ecmsinfo介绍
    帝国CMS排行榜调用标签
    通过案例理解position:relative和position:absolute
    帝国CMS万能标签标题截取后自动加入省略号
    linux 安装字体
    Ecms7.5版CK编辑器保留word格式如何修改
  • 原文地址:https://www.cnblogs.com/xqysckt/p/11385643.html
Copyright © 2020-2023  润新知