NOIP2009/道路游戏/洛谷P1070 题解

这是道十分神奇的dp...

我们以 $dp_i$ 表示第 $i$ 时刻所获的最大金币数,不难想到,可以采用刷表法,尝试在每一个 $i$ 时买一个新的,枚举它的起始位置和步数来更新后面的 $dp$ 值,但那样的复杂度就是 $O(n^3)$ 的,理论上过不了。

于是我们考虑优化,要删掉枚举步数的那一维,但首先我们需要改变更新的方式,改为由前面推后面的模式。于是有:

$dp_i={\rm{max}}(dp_{i-k}+g_{j-1,i}-g_{j-k-1,i-k}-a_{j-k})$

其中 $a$ 是购买机器人的花费,$j$ 表示此时购买的机器人的终点,$k$ 表示它走的步数。

关键是$g_{i,j}$表示价值的前缀和,表示‘过$i,j$的那一条\形对角线的在当前点的前缀和’,计算方式比较怪(可以继续往下看):

inline int back(int x,int t){
  return ((x-t)%n+n)%n;
}
for(int i=0;i<n;i++){
    for(int j=1;j<=m;j++)cin>>g[i][j];
}
for(int j=2;j<=m;j++){//注意!顺序不能变
  for(int i=0;i<n;i++)
     g[i][j]+=g[back(i,1)][j-1];
}

由于走一步时刻和位置都会增加1,机器人一段行程在时间-位置的二维坐标里是几条45度倾斜的\形斜线(因为有环),根据对角线,斜过来计算 $g$ 刚好可以覆盖每一段行程。而且这样解决了环的问题:更新时斜线碰到下边界时会自动换到最上面,每个 $g_{i,j}$ 都表示唯一的状态,调用时运用back就可以一步到位了。

ps:这里顺序如果行在外面,本该因为环形连起来的对角线会连不起来导致wa(上图左下角'3'和右侧'2 4 6',同位置的前缀和应如中图'3 5 9 15',下图为‘3 2 6 12’):


 

铺垫了这么多,我们终于可以开始优化了:把转移方程中带k的提出来,设:

$h_{i,j}=dp_i-g_{j-1,i}-a_j$

则:

$dp_i={\rm max}(h_{i-k,j-k})+g_{j-1,i}$

由于 $k$ 的范围是 $1-p$,可以把它丢到n个单调队列里,每条独立的对角线开一个,就可以通过了。

还有些细节看代码:

#include<bits/stdc++.h>
#define N 1009
#define M 50009
#define INF 0x3f3f3f3f
#define mod 998244353
using namespace std;
typedef long long ll;
typedef long double ldb;
typedef pair<int,int> pii;
int n,m,p;
int dp[N],g[N][N],a[N];
struct node{
  int q[N],head=1,tail=0,id[N];
}Q[N];
inline int back(int x,int t){
  return ((x-t)%n+n)%n;
}
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  cin>>n>>m>>p;
  for(int i=0;i<n;i++){
    for(int j=1;j<=m;j++)cin>>g[i][j];
  }
  for(int j=2;j<=m;j++){
    for(int i=0;i<n;i++)g[i][j]+=g[back(i,1)][j-1];
  }
  for(int i=0;i<n;i++)cin>>a[i];
  for(int i=0;i<n;i++){
    Q[back(i,-1)].q[++Q[back(i,-1)].tail]=-a[i];//j=0,设置每个时间和位置对应的单调队列,此时i对应i+1号单调队列
    Q[back(i,-1)].id[Q[back(i,-1)].tail]=0;//初始化,开始走之前买好机器人
  }
  for(int i=1;i<=m;i++){
    dp[i]=-INF;
    for(int j=0;j<n;j++){//一组(i,j),相当于i+1走了j步,所以倒退回去找属于他们的单调队列
      dp[i]=max(dp[i],Q[back(j,i-1)].q[Q[back(j,i-1)].head]+g[back(j,1)][i]);
    }
    for(int j=0;j<n;j++){
      while(Q[back(j,i-1)].head<=Q[back(j,i-1)].tail&&Q[back(j,i-1)].id[Q[back(j,i-1)].head]<=i-p)Q[back(j,i-1)].head++;//弹出过时元素
      while(Q[back(j,i-1)].head<=Q[back(j,i-1)].tail&&dp[i]-g[back(j,1)][i]-a[j]>=Q[back(j,i-1)].q[Q[back(j,i-1)].tail])Q[back(j,i-1)].tail--;//弹出不优元素
      Q[back(j,i-1)].q[++Q[back(j,i-1)].tail]=dp[i]-g[back(j,1)][i]-a[j];
      Q[back(j,i-1)].id[Q[back(j,i-1)].tail]=i;
    }
  }
  cout<<dp[m];
  return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity3D是一款强大的游戏开发引擎,能够用于开发各种类型的游戏,包括城市道路游戏。 在Unity3D中,我们可以使用多种工具和技术来创建逼真的城市道路。首先,可以使用Unity的内置工具创建道路,如建模工具、地形编辑器和材质编辑器等。这些工具可以帮助我们准确地建模和设计城市道路的形状、尺寸和风格。 其次,Unity3D支持使用物理引擎来实现真实的车辆行驶和道路碰撞效果。我们可以将车辆模型导入到场景中,并为其添加碰撞器和车辆控制脚本,实现车辆在道路上的行驶和与其他物体的交互。 此外,Unity3D还支持导航和路径规划功能,让车辆能够自主导航并遵循预定的路径行驶。通过使用Unity的导航系统,我们可以为车辆设置目的地和路径规则,使其能够自动行驶或遵循玩家指定的路线。 在设计城市道路游戏中,还可以利用Unity3D的粒子效果和特效系统来增强游戏的视觉效果。我们可以添加烟雾、火焰、雨水等特效,使游戏画面更加真实和生动。 最后,Unity3D还提供了丰富的音频和音效资源,可以为城市道路游戏增加环境音效、车辆引擎声音等,让玩家获得更加沉浸的游戏体验。 总之,Unity3D是一款功能强大的游戏开发引擎,可以实现逼真的城市道路游戏。通过利用其丰富的工具和技术,我们可以创建出真实的道路、实现车辆行驶和道路碰撞效果,并通过导航和特效系统提升游戏体验,使玩家感受到沉浸式的游戏乐趣。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值