0x00 题目来源
0x10 Tag
线性DP
0x20 题目描述
SERKOI最新推出了一种叫做“免费馅饼”的游戏:游戏在一个舞台上进行。舞台的宽度为 W W W格,天幕的高度为 H H H格,游戏者占一格。开始时游戏者站在舞台的正中央,手里拿着一个托盘。下图为天幕的高度为4格时某一个时刻游戏者接馅饼的情景。
游戏开始后,从舞台天幕顶端的格子中不断出现馅饼并垂直下落。游戏者左右移动去接馅饼。游戏者每秒可以向左或向右移动一格或两格,也可以站在原地不动。
馅饼有很多种,游戏者事先根据自己的口味,对各种馅饼依次打了分。同时,在8-308电脑的遥控下,各种馅饼下落的速度也是不一样的,下落速度以格/秒为单位。
当馅饼在某一秒末恰好到达游戏者所在的格子中,游戏者就收集到了这块馅饼。
写一个程序,帮助我们的游戏者收集馅饼,使得所收集馅饼的分数之和最大。
0x30 思路与算法
分析题义,我们很容易发现:对于 i i i秒时,其所得分为 i i i秒之前的得分加和的最大值与该时间游戏者所处位置 j j j的馅饼得分的总和。
而游戏者可以进行的操作是向左/右移动一到两格,游戏者在 i − 1 i-1 i−1秒可以通过以上五种方式(包括不动)移动到位置 j j j;可推出动态规划的状态转移方程: f ( i , j ) = m a x ( f ( i − 1 , j + k ) ) + a [ i ] [ j ] f(i,j)=max(f(i-1,j+k))+a[i][j] f(i,j)=max(f(i−1,j+k))+a[i][j]
( k ∈ [ − 2 , 2 ] ∧ k ∈ Z ) (k\in[-2,2] \wedge k\in Z) (k∈[−2,2]∧k∈Z)
其中 a [ i ] [ j ] a[i][j] a[i][j]表示该时间i游戏者所处位置 j j j的馅饼得分的总和。
直接转移复杂度为 O ( n 2 ) O(n^2) O(n2)
题目中馅饼可能不会恰好在某一秒末掉落到格子里( h % s p e e d ! = 0 h\%speed!=0 h%speed!=0),需要排除这种情况。
0x31 代码实现
变量
int h,w,m,cnt=1;
int dp[MAXN][MAXN];
int a[MAXN][MAXN];
int l[MAXN][MAXN];
其中h,w为题设中格子大小;m为最大所需时间;cnt为时间恰好落下的馅饼数量;a保持对应馅饼的分数;l记录位置
dp数组初始化:
for(int j=1;j<=w;++j)
dp[0][j]=-INF;
dp[0][w/2+1]=a[0][w/2+1];
对于除了游戏者起始位置之外的点,设为
−
I
N
F
-INF
−INF
实现细节
int f(int i,int j)
模拟了转移方程,其中的ans
初始化为
−
I
N
F
-INF
−INF
bool check(int x)
检查越界
void loc(int t,int pos)
递归实现记录路径
for(int j=1;j<=w;++j)
{
if(dp[m][j]>ans)
{
ans=dp[m][j];
pos=j;
}
}
由于不知道最后游戏者的位置,这里检索所有终点的分数,保存最大者。并保存终点,以便查找路径。
0x32 完整代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 0x7fffffff;
#define HACK freopen("test.in", "r", stdin);freopen("test.out", "w", stdout);
// #define LOCAL_JUDGE
const int MAXN=110;
int h,w,m,cnt=1;
int dp[MAXN][MAXN];
int a[MAXN][MAXN];
int l[MAXN][MAXN];
struct bing
{
int time,pos,speed,val;
}b[MAXN];
// save
bool check(int x)
{
return x>0&&x<=w;
}
int f(int i,int j)
{
int ans=-INF;
for(int k=-2;k<=2;++k)
{
if(check(j+k)&&ans<dp[i-1][j+k])
{
ans=dp[i-1][j+k];
l[i][j]=-k;
}
}
ans+=a[i][j];
return ans;
}
void loc(int t,int pos)
{
if(t<1) return;
loc(t-1,pos-l[t][pos]);
cout<<l[t][pos]<<endl;
}
int main()
{
#ifdef LOCAL_JUDGE
HACK;
#endif
scanf("%d%d",&w,&h);
h--;
while(scanf("%d%d%d%d",&b[cnt].time,&b[cnt].pos,&b[cnt].speed,&b[cnt].val)==4)
{
if((h%b[cnt].speed)==0)
{
b[cnt].time+=(h/b[cnt].speed);
cnt++;
}
}
cnt--;
// read
for(int i=1;i<=cnt;++i)
{
a[b[i].time][b[i].pos]+=b[i].val;
m=max(m,b[i].time);
}
for(int j=1;j<=w;++j)
dp[0][j]=-INF;
dp[0][w/2+1]=a[0][w/2+1];
//init
for(int i=1;i<=m;++i)
for(int j=1;j<=w;++j)
{
dp[i][j]=f(i,j);
}
// dp
int pos;
int ans=-INF;
for(int j=1;j<=w;++j)
{
if(dp[m][j]>ans)
{
ans=dp[m][j];
pos=j;
}
}
cout<<ans<<endl;
loc(m,pos);
//print
return 0;
}
0x40 另
h需要-1,馅饼是从最上面一格下落的,而不是点;
切记ans要设为-inf并非0(由于dp数组初始化为-inf)
调了四个小时 = =