zoukankan      html  css  js  c++  java
  • 洛谷P2389 电脑班的裁员(区间DP)

    题目背景

    隔壁的新初一电脑班刚考过一场试,又到了BlingBling的裁员时间,老师把这项工作交给了ZZY来进行。而ZZY最近忙着刷题,就把这重要的任务交(tui)给了你。

    题目描述

    ZZY有独特的裁员技巧:每个同学都有一个考试得分$ai(-1000<=ai<=1000)$,在n个同学$(n<=500)$中选出不大于k段$(k<=n)$相邻的同学留下,裁掉未被选中的同学,使剩下同学的得分和最大。要特别注意的是,这次考试答错要扣分【不要问我为什么】,所以得分有可能为负。

    输入输出格式

    输入格式:

    第一行为n,k,第二行为第1~n位同学的得分。

    输出格式:

    一个数s,为最大得分和。

    ---------------------------我是分割线-----------------------------

    强力安利出题人写的题解,里面竟然有$O(N)$的做法!

    我这个蒟蒻不才,只好写一下$O(N^3)$的DP维持一下生活。

    首先考虑建模,$f[i][j]$表示前i个数分成最多j个区间的最大价值,那么我们就可以枚举第j个区间的起点k,

    那么如果 $(k,i)>=0$ ,状态转移式子为: $f[i][j]=max(f[i][j],f[k-1][j-1]+sum(k,i))$

    如果 $(k,i)<0$ 就不选这个区间,式子为:$f[i][j]=max(f[i][j],f[k-1][j])$

    最后还有一个点,如果整个数列没有一个正数,答案就是0

    贴代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    int n,k,a[501],s[501],i,j,m;
    int f[501][501]; 
    int main(){
        scanf("%d%d",&n,&m);
        for (i=1; i<=n; i++){
            scanf("%d",&a[i]);
            s[i]=s[i-1]+a[i];
        }
        memset(f,-10,sizeof(f));
        for (i=1; i<=n; i++){
            for (j=1; j<=i; j++)
               for (k=1; k<=j; k++)
               f[i][1]=max(f[i][1],s[j]-s[k-1]);
        }
        for (i=1; i<=n; i++)
          for (j=2; j<=i; j++){
               for (k=j; k<=i; k++)
                   if (s[i]-s[k-1]>=0)
                       f[i][j]=max(f[i][j],f[k-1][j-1]+s[i]-s[k-1]);
                   else f[i][j]=max(f[i][j],f[k-1][j]);
               f[i][j]=max(f[i][j],f[i][j-1]);
            }
        printf("%d",max(f[n][m],0));
        return 0;
    }
  • 相关阅读:
    八数码问题
    Choose and divide
    紫书 动态规划例题
    3657 括号序列 动态规划
    动态规划 舞蹈家怀特
    分数规划
    Keywords Search AC自动机
    Minimum Transport Cost Floyd 输出最短路
    A Simple Problem with Integers 线段树 区间更新 区间查询
    CF519E A and B and Lecture Rooms
  • 原文地址:https://www.cnblogs.com/taduro/p/9462062.html
Copyright © 2011-2022 走看看