题目
题解
显然想到DP,设 f [ i ] [ j ] f[i][j] f[i][j]表示到了第 i i i行,第 j j j列时的最少点击数量
转移可以从两个地方转移:点了 x x x下和从上面降下来,降下来容易处理,点了 x x x下就需要去枚举 x x x,然后转移
降下来: f [ i ] [ j ] = m i n ( f [ i − 1 ] [ j + y [ i ] ] ) f[i][j]=min(f[i-1][j+y[i]]) f[i][j]=min(f[i−1][j+y[i]])注意 j + y [ i ] ≤ m j+y[i]\le m j+y[i]≤m
上去: f [ i ] [ j ] = m i n ( f [ i − 1 ] [ min ( m , j − x [ i − 1 ] ∗ k ) ] ) f[i][j]=min(f[i-1][\min(m,j-x[i-1]*k)]) f[i][j]=min(f[i−1][min(m,j−x[i−1]∗k)])
注意判断是否会撞到水管
但这样的时间复杂度是 O ( n m 2 ) O(nm^2) O(nm2),只能拿到 75 % 75\% 75%的数据
那么就需要优化
我们发现时间在枚举点击多少次的时候多了一层循环,那么我们考虑一下把这层循环去掉
发现上升其实是一个完全背包,那么转移就可以从两个地方转移:
f [ i ] [ j ] = m i n ( f [ i − 1 ] [ j − x [ i − 1 ] ] , f [ i ] [ j − x [ i − 1 ] ] ) + 1 f[i][j]=min(f[i-1][j-x[i-1]],f[i][j-x[i-1]])+1 f[i][j]=min(f[i−1][j−x[i−1]],f[i][j−x[i−1]])+1
下降转移不变
特判一下 j = m j=m j=m的情况
Code
#include<cstdio>
#include<algorithm>
#include<cstring>
#define inf 1e9
using namespace std;
struct node
{
int up,down;
}c[10005];
int n,m,q,pos,down,up,ans,x[10005],y[10005],num[10005],f[10005][1005];
bool g[10005][1005];
bool pd(int h,int w)
{
if (h<=c[w].down) return false;
if (h>=c[w].up) return false;
return true;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for (int i=0;i<n;++i)
scanf("%d%d",&x[i],&y[i]);
for (int i=1;i<=q;++i)
{
scanf("%d%d%d",&pos,&down,&up);
c[pos].down=down;
c[pos].up=up;
}
for (int i=1;i<=n;++i)
{
if (!c[i].up) c[i].up=m+1,num[i]=num[i-1];
else num[i]=num[i-1]+1;
}
for (int i=1;i<=n;++i)
for (int j=0;j<=m;++j)
f[i][j]=inf;
f[0][0]=inf;
for (int i=1;i<=n;++i)
{
for (int j=1;j<=m;++j)
{
if (j>=x[i-1])
{
f[i][j]=min(f[i][j],f[i-1][j-x[i-1]]+1);
f[i][j]=min(f[i][j],f[i][j-x[i-1]]+1);
}
if (j==m)
{
for (int k=j-x[i-1];k<=m;++k)
{
f[i][j]=min(f[i][j],f[i-1][k]+1);
f[i][j]=min(f[i][j],f[i][k]+1);
}
}
}
for (int j=c[i].down+1;j<c[i].up;++j) if (j+y[i-1]<=m) f[i][j]=min(f[i][j],f[i-1][j+y[i-1]]);
for (int j=1;j<=c[i].down;++j) f[i][j]=inf;
for (int j=m;j>=c[i].up;--j) f[i][j]=inf;
}
ans=inf;
for (int i=1;i<=m;++i)
ans=min(ans,f[n][i]);
if (ans==inf)
{
printf("0\n");
for (int i=n-1;i>=0;--i)
for (int j=1;j<=m;++j)
if (f[i][j]!=inf)
{
printf("%d\n",num[i]);
return 0;
}
}
else printf("1\n%d\n",ans);
return 0;
}