分析题意,可以发现:按屏幕的过程类似于“取”与“不取”,柱子则是一些“限制条件”和“范围”。
思路
首先明确:
1、一秒可以无限点上升键(完全背包),并且一旦上升就不会下降Y。
一秒只能下降一次,不用点(01背包)。
2、碰到地面会死(处理下降时),碰到天花板不会死(处理上升时)
设(f[i][j])为在第i列时,所处高度为i可行的最少点击屏幕数。
分段进行状态转移:(手打公式好累啊)
( f[i][j]=min (f[i][j-X[i]]+1,f[i-1][j-X[i]]+1)quad(X[i]+1leq jleq m+X[i]))
//可以按一次(01),也可以按两次以上(完全)。碰到天花板不会死,所以还要更新([m+1,m+X[i]])的状态。
(f[i][j]=min(f[i][j],f[i][m])quad (m+1leq jleq m+X[i]))
//([m+1,m+X[i]])范围内的状态效果与(f[i][m])(正好跳到天花板)相同。如果正好跳到天花板更优则更新。
( f[i][j]=min(f[i][j],f[i-1][j-Y[i])quad (1leq jleq m-Y[i]))
//01背包,从(Y[i])单位长度上方掉落。
$ Inf quad (jleq L[i],H[i]leq j)$
//到不了的,碰到柱子的设为(Inf)
代码
#include<iostream>
#include<iomanip>
#include<cmath>
using namespace std;
const int maxn=10005,maxm=2005;
int f[maxn][maxm],L[maxn],H[maxn],X[maxn],Y[maxn];
int n,m,k,p,cnt,ans,temp;
bool flag;
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
cin>>X[i]>>Y[i],L[i]=0,H[i]=m+1;//没有柱子也要设置上下界
for(int i=1;i<=k;i++)
cin>>p,cin>>L[p]>>H[p];
for(int i=1;i<maxn;i++)
for(int j=1;j<maxm;j++)
f[i][j]=114514;
//输入,设初值
for(int i=1;i<=n;i++)
{
for(int j=X[i]+1;j<=m+X[i];j++)
f[i][j]=min(f[i-1][j-X[i]]+1,f[i][j-X[i]]+1);//可以按一次(01),也可以按两次以上(完全)。碰到天花板不会死,所以还要更新[m+1,m+X[i]]的状态。
for(int j=m+1;j<=m+X[i];j++)
f[i][m]=min(f[i][j],f[i][m]);//[m+1,m+X[i]]范围内的状态效果与f[i][m](正好跳到天花板)相同。如果正好跳到天花板更优则更新。
for(int j=1;j<=m-Y[i];j++)
f[i][j]=min(f[i][j],f[i-1][j+Y[i]]);//01背包,从$Y[i]$单位长度上方掉落。
for(int j=1;j<=L[i];j++) f[i][j]=114514;
for(int j=H[i];j<=m;j++) f[i][j]=114514;
//到不了的,碰到柱子的设为Inf
}
ans=114514;
for(int i=1;i<=m;i++)
ans=min(ans,f[n][i]);
if(ans<114514)
cout<<1<<"
"<<ans;
else {
cout<<"0
";
cnt=0;
for(int i=n;i>=0;i--)
{
for(int j=1;j<=m;j++)
if(f[i][j]<114514)
{
flag=1;
break;//从后往前搜,一旦有一处能到达,就说明这之前的柱子都能通过
}
if(flag)
{
cnt=i;
break;
}
}
for(int i=1;i<=cnt;i++)
if(H[i]<=m) temp++;//统计柱子数
cout<<temp;
}
return 0;
}
注:数据范围允许使用【数据删除】作为Inf使用