一、题目链接:
二、题目大意
游戏界面是一个长为
n
n
n,高为
m
m
m的二维平面,其中有
k
k
k个管道(忽略管道的宽度)。小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。
小鸟每个单位时间沿横坐标方向右移的距离为
1
1
1,竖直移动的距离由玩家控制。
- 如果点击屏幕,小鸟就会上升一定高度 x x x,每个单位时间可以点击多次,效果叠加
- 如果不点击屏幕,小鸟就会下降一定高度 y y y
小鸟位于横坐标方向不同位置时,上升的高度
x
x
x和下降的高度
y
y
y可能互不相同。小鸟高度等于
0
0
0或者小鸟碰到管道时,游戏失败。小鸟高度为
m
m
m时,无法再上升,游戏继续。
现在,请你判断是否可以完成游戏:
- 如果可以完成,输出最少点击屏幕数
- 如果不能完成,输出小鸟最多可以通过多少个管道缝隙
数据范围: 5 ≤ n ≤ 10000 5\leq n\leq 10000 5≤n≤10000, 5 ≤ m ≤ 1000 5\leq m\leq 1000 5≤m≤1000
三、题目分析
一个很显然的结论是,按照水平方向,后面的状态的值不影响前面的状态,所以这是一个很典型的动态规划问题,再结合一下数据范围大致要求时间复杂度在 O ( n m ) O(nm) O(nm),同样符合动态规划的特征。
下面考虑如何转移,因为题目非常的明确,两层之间只有两种转移方式。所以我们很容易将此问题转化为背包问题进行求解。将高度作为背包的容量,每一层的选择作为物品,点击的次数作为价值。
下降即相当于从背包中取出,而上升则是正常的放入物品。由于题目要求,上升是可以累计多次点击来上升的。所以下降采用
01
01
01背包问题的解法,而上升采用完全背包问题的解法。
状态转移方程如下:
d
p
[
i
]
[
m
i
n
(
j
,
m
)
]
=
m
i
n
(
d
p
[
i
]
[
j
−
x
[
i
−
1
]
]
+
1
,
d
p
[
i
−
1
]
[
j
−
x
[
i
−
1
]
]
+
1
)
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
i
−
1
]
[
j
+
y
[
i
−
1
]
]
)
\begin{gather} dp[i][min(j,m)]=min(dp[i][j-x[i-1]]+1,dp[i-1][j-x[i-1]]+1)\\ dp[i][j]=min(dp[i][j],dp[i-1][j+y[i-1]]) \end{gather}
dp[i][min(j,m)]=min(dp[i][j−x[i−1]]+1,dp[i−1][j−x[i−1]]+1)dp[i][j]=min(dp[i][j],dp[i−1][j+y[i−1]])
四、注意事项:
从某种程度上来讲这是一道很简单的题目,然而它有很多坑:
- 首先就是状态转移方程的 m i n ( j , m ) min(j,m) min(j,m),因为题目只说了高度不能超过 m m m,不是说当高度 j + x [ i ] > m j+x[i]>m j+x[i]>m时不能点击上升了,而只是点击了之后最多变成m,多余部分舍弃而已。
- 因为完全背包的问题,上升的方程里有层内转移,而实际上升的过程是在前一层进行的,所以可能存在中间状态非法,但是最终状态成立的情况,所以我们最后转移完之后再将非法状态重置,而不是在一开始不允许非法状态参与转移。
- 最大的问题还是多重背包的层内转移问题,所以我们不能先转移下降的状态,否则就会变成层与层之间既进行了上升又进行了下降,而这是不允许同时存在的。
五、正解代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
typedef int ll;
const ll maxn=10010;
const ll maxm=2010;
const ll inf=1e9;
struct node
{
ll pos;
ll l;
ll h;
}pile[maxn];
ll n,m,K;
ll x[maxn],y[maxn];
ll dp[maxn][maxm];//在x=i,y=j位置的最少点击次数
int main()
{
memset(pile,0,sizeof(pile));
scanf("%d%d%d",&n,&m,&K);
for(ll i=0;i<=n-1;i++)
scanf("%d%d",&x[i],&y[i]);
for(ll i=1;i<=K;i++)
{
node temp;
scanf("%d%d%d",&temp.pos,&temp.l,&temp.h);
pile[temp.pos]=temp;
}
for(ll i=1;i<=n;i++)
for(ll j=1;j<=m;j++)
dp[i][j]=inf;
for(ll i=1;i<=m;i++)
dp[0][i]=0;
ll ans=inf,Max=0;
for(ll i=1;i<=n;i++)
{
for(ll j=x[i-1]+1;j<=m+x[i-1];j++)
{
ll act=min(m,j);
dp[i][act]=min(dp[i][act],dp[i-1][j-x[i-1]]+1);
dp[i][act]=min(dp[i][act],dp[i][j-x[i-1]]+1);
}
for(ll j=1;j<=m;j++)
if(!(pile[i].pos==0 || (j>pile[i].l && j<pile[i].h)))
dp[i][j]=inf;
for(ll j=1;j<=m;j++)
if(j+y[i-1]<=m)
dp[i][j]=min(dp[i][j],dp[i-1][j+y[i-1]]);
for(ll j=1;j<=m;j++)
{
if(!(pile[i].pos==0 || (j>pile[i].l && j<pile[i].h)))
dp[i][j]=inf;
if(dp[i][j]!=inf)
Max=max(Max,i);
if(i==n && dp[i][j]!=inf)
ans=min(ans,dp[i][j]);
}
}
if(ans==inf)
{
printf("0\n");
ll fina=0;
for(ll i=1;i<=Max;i++)
if(pile[i].pos!=0)
fina++;
printf("%d\n",fina);
}
else
printf("1\n%d\n",ans);
return 0;
}