解题报告:
这道题很容易想出一个
O(NM2)
的动规
即f[i][j]表示飞到i列j高度位置需要的最小点击数,则
f[i][j]=min(f[i−1][j−k∗x]+k),1≤k≤j/x(O(M)) (不考虑掉下来的情况)
这显然会超时。
仔细分析,我们可以发现i-1列的格子被计算了很多次,越下面的格子被算的次数越多到最下面时几乎所有各自都要用他算。这便是我们优化的突破口。
f[i][j]=min(f[i−1][j−k∗x]+k),1≤k≤j/x
f[i][j−x]=min(f[i−1][(j−x)−(k−1)∗x]+(k−1)),2≤k≤j/x
对比以上式子,我们可以发现当k≥2 时
f[i][j]=f[i][j−x]+1
于是我们得到了崭新的状态转移方程:
f[i][j]=min(f[i−1][j−x],f[i][j−x])+1(O(1))
(不考虑掉下来的情况)
O(M)地处理完一列后再O(M)地扫一遍,检查掉下来更优的情况。
总复杂度O(NM)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=10005,M=1005,INF=0x3f3f3f3f;
int n,m,k,ans,cnt;
int x[N],y[N],down[N],up[N];
int f[N][M];
int main()
{
//freopen("lx.in","r",stdin);
n=getint(),m=getint(),k=getint();
for(int i=0;i<n;i++)
{
x[i]=getint(),y[i]=getint();
down[i]=0,up[i]=m+1;
}
down[n]=0,up[n]=m+1;
for(int i=1;i<=k;i++)
{
int p=getint();
down[p]=getint(),up[p]=getint();
}
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
f[i][j]=INF;
for(int i=1;i<=n;i++)
{
for(int j=x[i-1]+1;j<=m;j++)
f[i][j]=min(f[i][j],min(f[i-1][j-x[i-1]]+1,f[i][j-x[i-1]]+1));
for(int j=m-x[i-1];j<=m;j++)
f[i][m]=min(f[i][m],min(f[i-1][j]+1,f[i][j]+1));
for(int j=down[i]+1;j<up[i];j++)
if(j+y[i-1]<up[i-1])f[i][j]=min(f[i][j],f[i-1][j+y[i-1]]);
for(int j=0;j<=down[i];j++)f[i][j]=INF;
for(int j=up[i];j<=m;j++)f[i][j]=INF;
}
ans=INF,cnt=k;
for(int i=n;i>=1;i--)
{
for(int j=down[i]+1;j<up[i];j++)
ans=min(ans,f[i][j]);
if(ans!=INF)break;
if(up[i]!=m+1)cnt--;
}
if(cnt==k)cout<<1<<'\n'<<ans;
else cout<<0<<'\n'<<cnt;
return 0;
}