zoukankan      html  css  js  c++  java
  • 动态规划---区间dp

    今天写内网题,连着写了两道区间dp,这里就总结一下。

    区间dp思想主要是先枚举f[i][j]中的i,再枚举j,再枚举一个1~j之间的变量k,一般是f[i][j] = max(f[i][j],f[i][k] + f[k][j]);(石子合并)

    但是今天遇到的两个都不是这样的。

    第一题,复制书稿,洛谷P1282。猜到是区间dp了,但是没写出来。后来看了一下,f[i][j]代表前i个人写到j本书,枚举k为第i个人从第k本书开始写。这样转移方程就很好想了。f[i][j] = min(f[i][j],max(f[i - 1][l],a[l  -  1] + a[l] + a[l + 1] + ... + a[j]),这样复杂度有点高,所以后半部分用前缀和来维护就行了。

    代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    int f[510][510];
    int a[510],m,n,sum[510];
    void print(int x, int Ans)
    {
        if(!x) return;
        for(int i=x; i>=0; i--)
        {
            if(sum[x] - sum[i-1] > Ans || !i)
            {
                print(i, Ans);
                printf("%d %d
    ", i+1, x);
                break;
            }
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d",&a[i]);
        }
        memset(f,127,sizeof(f));
        for(int i = 1; i <= n; i++)
            sum[i] = sum[i - 1] + a[i],f[1][i] = sum[i];
        for(int i = 2; i <= m; i++)
        {
            for(int j = i; j <= n; j++)
            {
                for(int l = i; l <= j; l++)
                {
                    f[i][j] = min(f[i][j],max(f[i - 1][l - 1],sum[j] - sum[l - 1]));
                }
            }
        }
        print(n,f[m][n]);
        return 0;
    }
    /*
    9 3
    1 2 3 4 5 6 7 8 9
    */

    第二题,机器分配,到现在我也不知道为什么是区间dp,但是也只能硬着头皮写了。

    第一次,想正常思路,f[i][j]代表i个公司分配j个机器。中间枚举第i个公司分配k个机器。但是貌似过不去,只能的90,因为要按照字典序输出。这个题需要倒着枚举。

    #include<cstdio>
    #include<iostream>
    using namespace std;
    int f[50][50],path[15][20][15];
    int num[50][50],n,m;
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j <= m;j++)
                scanf("%d",&num[i][j]);
        }
        for(int i = 1;i <= n;i++)
        {
            for(int j = 0;j <= m;j++)
            {
                for(int k = 0;k <= j;k++)
                {
                    if(f[i - 1][j - k] + num[i][k] > f[i][j])
                    {
                        f[i][j] = f[i - 1][j - k] + num[i][k];
                        for(int h = 1;h < i;h++)
                        path[i][j][h] = path[i - 1][j - k][h];
                        path[i][j][i] = k;
                    }
                }
            }
        }
        cout<<f[n][m]<<endl;
        for(int i = 1;i <= n;i++)
        {
            cout<<i<<" "<<path[n][m][i]<<endl;
        }
        return 0;
    }

    倒着枚举就是f[i][j]代表i各公司不给j个,然后就好了。

    #include<cstdio>
    #include<iostream>
    using namespace std;
    int f[50][50],path[15][20][15];
    int num[50][50],n,m;
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j <= m;j++)
                scanf("%d",&num[i][j]);
        }
        for(int i = 1;i <= n;i++)
        {
            for(int j = 0;j <= m;j++)
            {
                for(int k = 0;k <= j;k++)
                {
                    if(f[i - 1][k] + num[i][j - k] > f[i][j])
                    {
                        f[i][j] = f[i - 1][k] + num[i][j - k];
                        for(int h = 1;h < i;h++)
                        path[i][j][h] = path[i - 1][k][h];
                        path[i][j][i] = j - k;
                    }
                }
            }
        }
        cout<<f[n][m]<<endl;
        for(int i = 1;i <= n;i++)
        {
            cout<<i<<" "<<path[n][m][i]<<endl;
        }
        return 0;
    }
  • 相关阅读:
    CYQ.Data 轻量数据层之路 V4.0 版本发布
    基于MSAA的自动化封装和设计—python版(转)
    【自然框架】之鼠标点功能现(二):表单控件的“应用”—— 代码?只写需要的!
    论管理员的不作为!!!
    【自然框架】之通用权限的Demo(二):添加人员、添加账户、添加角色里面的账户以及列表的权限验证
    使用接口来统一控件的取值、赋值和初始化
    【自然框架】之通用权限(八):权限到字段(列表、表单、查询)
    辩论赛 VS 讨论组
    【自然框架】表单控件 之 一个表单修改多个表里的记录
    【自然框架】之“解耦”初探
  • 原文地址:https://www.cnblogs.com/DukeLv/p/9170245.html
Copyright © 2011-2022 走看看