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:
*/