zoukankan      html  css  js  c++  java
  • DP套题练习1

    前言:练习①不难,但也有注意的地方.

    Q1: 给定AOE网络工程图,求完成时间及其中的关键工程.

    S1:先拓扑排序[记得用队列,O(n)的复杂度],确定DP的顺序(后效性).DP方程显然为:f[ to ] = max( f[ to ] , f[ x ] + val[ to ] ).求关键工程则逆推DP状态的转移过程.

    细节:注意最后可能有多条路劲同时完成,要注意处理.

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<queue>
    using namespace std;
    #define e exit(0)
    #define R register
    int n,cnt,cnt1,deep,ans,id,lenth,val[210],head[210],head1[210],rd[210],dl[210],cd[210],s[210][210],f[210],ask[210],vis[210];
    struct bian{
        int to,next;
    }len[210*210];
    struct bian1{
        int to,next;
    }len1[210*210];
    void add(int from,int to)
    {
        len[++cnt].to=to;
        len[cnt].next=head[from];
        head[from]=cnt;
    }
    void add1(int from,int to)
    {
        len1[++cnt1].to=to;
        len1[cnt1].next=head1[from];
        head1[from]=cnt1;
    }
    void tpsort()
    {
        queue<int> q;
        for(R int i=1;i<=n;++i)
            if(!rd[i]){
                q.push(i);
                dl[++deep]=i;
                f[i]=val[i];
            }
        while(q.size()){
            int now=q.front();
            q.pop();
            for(R int k=head[now];k;k=len[k].next){
                int to=len[k].to;
                --rd[to];
                if(rd[to]==0){
                    dl[++deep]=to;
                    q.push(to);
                }
            }
        }
        if(deep!=n){
            printf("-1");
            exit(0);
        }
    }
    void dfs(int x)
    {
        for(R int k=head1[x];k;k=len1[k].next){
            int to=len1[k].to;
            if(f[x]==f[to]+val[x]){
                if(!vis[to]){
                    ask[++lenth]=to;
                    vis[to]=1;
                    dfs(to);
                }
            }
        }
    }
    int main()
    {
        freopen("project.in","r",stdin);
        freopen("project.out","w",stdout);
        scanf("%d",&n);
        for(R int i=1;i<=n;++i)
            scanf("%d",&val[i]);
        for(R int i=1;i<=n;++i)
            for(R int j=1;j<=n;++j)
                if(i!=j)
                    s[i][++s[i][0]]=j;
        for(R int i=1;i<=n;++i){
            int to=i;
            for(R int j=1;j<=n-1;++j){
                int from=s[i][j],num;
                scanf("%d",&num);
                if(num==0) 
                    continue;
                add(from,to),add1(to,from);
                ++rd[to];
                ++cd[from];
            }
        }
        tpsort();
        for(R int i=1;i<=deep;++i){
            int now=dl[i];
            for(R int k=head[now];k;k=len[k].next){
                int to=len[k].to;
                f[to]=max(f[to],f[now]+val[to]);
            }
        }
        for(R int i=1;i<=n;++i)
            if(!cd[i]){
                if(f[i]>ans)
                    ans=f[i];
            }
        for(R int i=1;i<=n;++i)
            if(!cd[i]&&!vis[i]){
                if(f[i]==ans)
                    ask[++lenth]=i,vis[i]=1;
            }
        printf("%d
    ",ans);
        for(R int i=1;i<=lenth;++i)
            dfs(ask[i]);
        sort(ask+1,ask+1+lenth);
        for(R int i=1;i<=lenth;++i)
            printf("%d ",ask[i]);
        return 0;
    }

    Q2:某总公司拥有高效生产设备 M 台,准备分给下属的 N 个分公司.各分公司若获得这些设备,可以为总公司提供一定的盈利.问:如何分配这 M 台设备才能使国家得到的盈利最大?求出最大盈利值.分配原则:每个公司有权获得任意数目的设备,但总台数不得超过总设备数 M。其中M<=100,N<=100.

    S2:显然的线性DP,可以借用背包的思想,设f[ i ][ j ]表示我们前 i 个公司共选了j台设配获得的最大收益.枚举当前选入设备总台数k与上一次总台数k,便有了转移方程f[ i ][ j ] = max(f[ i ][ j ],f[ i - 1][ k ]+val[ j -k] ).注意初始化: f[ 1 ][ j ]=val[1][ j ].

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    #define e exit(0)
    #define R register
    int m,n,ans,f[110][110],val[110][110];
    int main()
    {
        freopen("machine.in","r",stdin);
        freopen("machine.out","w",stdout);
        scanf("%d%d",&m,&n);
        for(R int i=1;i<=n;++i)
            for(R int j=1;j<=m;++j)
                scanf("%d",&val[i][j]);
        for(R int j=1;j<=m;++j)
            f[1][j]=val[1][j];
        for(R int k=1;k<=n;++k)
            for(R int i=0;i<=m;++i)
                for(R int j=0;j<=i;++j)
                    f[k][i]=max(f[k][i],f[k-1][j]+val[k][i-j]);
        for(R int j=0;j<=m;++j)
            ans=max(ans,f[n][j]);
        printf("%d",ans);
        return 0;
    }

    Q3:给定字符串S,T.给定操作替换,插入,删除,每进行其中之一为一次操作,问最少多少次操作S变成T.

    S3:对于两个字符串之间的DP,我们一般会设f[ i ][ j ]表示字符串S的前 i 位与T的前 j 位进行xxx操作,维护max/min值.这题我们顺这思路想,设f[ i ][ j ]为将字符串S的前 i 位变成T的前 j 位最少操作数.如果s[ i ]==t[ j ],f[ i ][ j ]=f[ i-1][ j-1].如果s[ i ]!=t[ j ],则进行三种操作.①f[ i ][ j ]=min{f[ i -1][ j-1]+1},表示直接替换.②f[ i ][ j ]=min{f[ i ][ j-1]+1},表示将S的前i位变成T的前j-1位,再加一位.③f[ i ][ j ]=min{f[ i -1][ j ]+1},表示将S的前i-1位变成T的前j位,再删一位.

    细节:注意初始化,f[ 0 ][ j ] = j, 0 位变成 j 位显然要用 j 次操作,f[ i ][ 0 ] = i 则同理.

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    #define e exit(0)
    #define R register
    char s[1010],t[1010];
    int lens,lent,f[1010][1010];
    int main()
    {
        freopen("edit.in","r",stdin);
        freopen("edit.out","w",stdout);
        scanf("%s",s+1),scanf("%s",t+1);
        lens=strlen(s+1),lent=strlen(t+1);
        memset(f,0x7f,sizeof(f));
        f[0][0]=0;
        for(R int i=1;i<=lens;++i)
            f[i][0]=i;
        for(R int j=1;j<=lent;++j)
            f[0][j]=j;
        for(R int i=1;i<=lens;++i)
            for(R int j=1;j<=lent;++j){
                if(s[i]==t[j])
                    f[i][j]=f[i-1][j-1];
                else if(s[i]!=t[j]){
                    f[i][j]=min(f[i][j],f[i-1][j-1]+1);
                    f[i][j]=min(f[i][j],f[i][j-1]+1);
                    f[i][j]=min(f[i][j],f[i-1][j]+1);
                }
            }
        printf("%d",f[lens][lent]);
        return 0;
    }

    Q4:给n个硬币面值,求对于面值v,我们最少用多少枚硬币表示,若不能则找出能表示的次小面值v',求其硬币表示数,硬币可重复使用.

    S4:显然完全背包的模板,我们改下定义即可,f[ i ]表示面值i能被表示的最少硬币数,f[ i ] = min{f[ i -cost ]+1}.注意初始化原始面值的f数组都为1.

    细节:当f[i-cost]==0,此状态不合法,舍去.

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    #define R register
    int n,v,val[60],f[100010];
    int main()
    {
        freopen("coin.in","r",stdin);
        freopen("coin.out","w",stdout);
        scanf("%d%d",&n,&v);
        memset(f,0x7f,sizeof(f));
        for(R int i=1;i<=n;++i){
            scanf("%d",&val[i]);
            f[val[i]]=1;
        }
        for(R int i=1;i<=n;++i)
            for(R int j=val[i];j<=v;++j){
                if(f[j-val[i]]==0)
                    continue;
                f[j]=min(f[j],f[j-val[i]]+1);
            }
        if(f[v]!=2139062143)
            printf("%d",f[v]);
        else if(f[v]==2139062143){
            for(R int i=v-1;i>=1;--i)
                if(f[v]!=2139062143){
                    printf("%d",f[v]);
                    break;
                }
        }
        return 0;
    }
  • 相关阅读:
    物流与仓库
    测试使用
    禅修的升级
    《引爆点 马尔科姆 格拉德威尔》读书笔记总结----《创业必读书第20本》--创业第三关做好业务:3,如何做好营销和增长第4本
    shell
    Vue中常用的方法记录
    前端工程化3-tapable
    Browser上传文件到华为云/七牛云 详细步骤
    immutable
    shell利用叮叮发送消息
  • 原文地址:https://www.cnblogs.com/xqysckt/p/11366583.html
Copyright © 2011-2022 走看看