保龄球
你一个人保龄球馆去打保龄球。总共有k个球可用。每个球的宽度为w。在你前方有n个球棒要打。这n球棒紧密的排成一行,且第i个球棒宽度为1,价值为xi。你的每个球恰能击中第a个~第a+w-1个的球棒(如果此球棒存在的话)。球棒被打到就倒了,且互不影响。你可以向任意方向击球,甚至球的一部分可以越过最左、最右边球棒所构成的边界。求最大价值。
输入格式
文件第一行是三个整数n,k,w。以下n行是n个整数,第i个代表xi。
输出格式:
仅一行,为最大价值。
输入样例
92 3
2
8
5
1
9
6
9
3
2
输出样例
39
数据范围
40%的数据满足1≤n,w,k≤100;
100%的数据满足1≤n≤10000,1≤w≤100,1≤k≤500;
一开始WA20,因为少考虑了一个转移。
这道题写了我很久,目前40分,超时6组。
f[i][j] = max{ f[i-1][l]+sum[j]-sum[j-w],(l∈[0,j-w-1])
f[i-1][l]+sum[j]-sum[l] ,(l∈[j-w,j-1])}。
即前i个保龄球打前j个棒。
第一个转移容易理解,即打的范围是w。
第二个转移我忽略了,就是有可能打的区间有重复。
对于左边界和右边界那两个条件,可以进行两种不同的处理。
对于左边界f[l][i] = sum[i]-sum[i-w]
对于右边界用和青蛙过河一样的处理,将枚举范围扩大为n+w,最后在n~n+w中间找最优解就行了。
#include <iostream>
using std::cout;
//using std::cin;
#include <cstdio>
const long oo = 0x7fff0000;
typedef long long lld;
long n;long k;long w;
long x[10010];
lld sum[10010];
lld f[510][10010];
int main()
{
freopen("bowling.in","r",stdin);
freopen("bowling.out","w",stdout);
scanf("%ld%ld%ld",&n,&k,&w);
for (long j=0;j<n+w+1;j++)
{
for (long i=0;i<k+1;i++)
{
f[i][j] = -926359016;
}
}
for (long i=1;i<n+1;i++)
{
scanf("%ld",x+i);
sum[i] = sum[i-1]+(lld)x[i];
}
for (long i=n+1;i<n+w+1;i++)
{
sum[i] = sum[i-1];
}
lld ans=-oo;
for (long i=1;i<n+w+1;i++)
{
f[1][i] = sum[i]-sum[i-w>?0];
ans >?= f[1][i];
}
for (long i=2;i<k+1;i++)
{
for (long j=1;j<n+w+1;j++)
{
for (long l=0;l<=j-w-1>?0;l++)
{
f[i][j] >?= f[i-1][l]+sum[j]-sum[j-w>?0];
}
for (long l=j-1;l>=((j-w)>?0);l--)
{
f[i][j] >?= f[i-1][l]+sum[j]-sum[l];
}
ans >?= f[i][j];
}
}
cout << ans;
return 0;
}