算法入门DP-免费馅饼

0x00 题目来源

1006-免费馅饼

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 i1秒可以通过以上五种方式(包括不动)移动到位置 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(i1,j+k))+a[i][j]

( k ∈ [ − 2 , 2 ] ∧ k ∈ Z ) (k\in[-2,2] \wedge k\in Z) (k[2,2]kZ)
其中 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)
调了四个小时 = =

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值