POJ 2228 Naptime (DP) #by Plato

http://poj.org/problem?id=2228

题意:从N个元素(环形队列)中选出B个,求最大得分。(元素得分仅当其前面的元素也被选择了)

Shine:分类讨论的思想,边界处理

Idea;

假如是线性队列的话,Dp方程很容易写出:

f[i][j][1] = max{f[i-1][j-1][1] + U[i],f[i-1][j-1][0]}

f[i][j][0] = max{f[i-1][j-1][1],f[i-1][j][0]}

处理环形队列比较麻烦,首先想到是扩展为双倍长的线性,WA

以每个元素为起点分别做一次DP,会TLE的

这里应该用分类讨论的思想:

把它当做线性队列,这样就没有给U【1】被选上的机会,所以:

1.不给U[1]机会,U[1]一定不被选上,U[N]随便

2.给U[1]机会,U[N]一定被选上,U[1]随便

这样就是两次DP了。


另外,状态要用滚动数组来保存。(滚动数组有更好的写法,无视我的吧)

两次DP的边界不一样、再加上滚动数组,使得边界变得很难写了(我就WA这里好多次)

1.f[1][...][...] = -INF, f[1][1][1] = 0, f[1][0][0] = 0; f[...][0][0] = 0; f[...][0][1] = -INF;

2.f[1][...][..] = -INF, f[1][1][1] = U[1]; f[1][0][0] = 0; f[...][0][0] = 0 ; f[...] [ 0][1] = -INF;


#include <cstdio>
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;

#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
const int INF = (1<<31)-1;

int main()
{
    freopen("test.txt","r",stdin);
    int N,B;
    static int U[4000];
    static int arr1[4000][2],arr2[4000][2];
    while (~scanf("%d%d",&N,&B))
    {
        for (int i = 1;i <= N;i++)
        {
            scanf("%d",&U[i]);
        }

        int (*pre)[2] = arr1,(*next)[2] = arr2,(*temp)[2];

        for (int i = 0;i <= B;i++)
        {
            pre[i][0] = pre[i][1] = -INF;
        }
        pre[1][1] = pre[0][0] = 0;
        for (int i = 2;i <= N;i++)
        {
            next[0][0] = 0;
            next[0][1] = -INF;
            for (int j = 1;j <= B;j++)
            {
                next[j][0] = MAX(pre[j][0],pre[j][1]);
                next[j][1] = pre[j-1][0];
                if (pre[j-1][1] + U[i] > next[j][1])
                    next[j][1] = pre[j-1][1] + U[i];
            }
            temp = pre;
            pre = next;
            next = temp;
        }
        int maxx = MAX(pre[B][0],pre[B][1]);

        for (int i = 0;i <= B;i++)
        {
            pre[i][0] = pre[i][1] = -INF;
        }
        pre[1][1] = U[1];
        pre[0][0] = 0;
        for (int i = 2;i <= N;i++)
        {
            next[0][0] = 0;
            next[0][1] = -INF;
            for (int j = 1;j <= B;j++)
            {
                next[j][0] = MAX(pre[j][0],pre[j][1]);
                next[j][1] = pre[j-1][0];
                if (pre[j-1][1] + U[i] > next[j][1])
                    next[j][1] = pre[j-1][1] + U[i];
            }
            temp = pre;
            pre = next;
            next = temp;
        }

        int ans = MAX(maxx,pre[B][1]);
        printf("%d\n",ans);
    }

    return 0;
}
/*
example/WA:f[0][0][1]是一个不存在的状态,没有去进行特殊处理
1st/WA:环形序列扩展两倍后成为线性序列,最后统计结果时还是按一倍线性进行了
2nd/WA:
*/

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值