题目传送门:https://www.luogu.org/problem/show?pid=1941
不难看出,此题为动态规划。
我们用$f[i][j]$表示从第一行到达第i行第j列时点击屏幕的最少次数。
不难推出,$f[i][j]=min(f[i-1][j+y[i-1]],f[i-1][j-k*x[i-1]]+k)$,其中k为整数,且$j+y[i-1]$,$j-k*x[i-1]$需在合法范围内,$(i,j)$不能是柱子。
显然,这个转移的时间复杂度是$O(n*m^2)$的,只有70分,下面考虑进行优化。
先考虑只向上跳的情况,对于转移$f[i][j]=f[i-1][j-2x[i-1]+2$,我们不妨设$f[i][1..j]和f[1..i][1..m]$已经全部是最优解,那么在考虑$f[i][j]$时,只需要判断$f[i-1][j-x[i-1]$和$f[i][j-x[i-1]$哪个更优即可。
对于每一列,处理完向上跳的情况后,再处理向下滑行的状态。(我这里写挫了只剩下75,只能说数据实在太水)
PS:对于鸟贴顶的情况,需要特殊判断。
这样时间复杂度为$O(n*m)$。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define M 10100 5 using namespace std; 6 int f[M][1010]={0},b[M][1010]={0},n,m,k; 7 int x[M]={0},y[M]={0}; 8 9 int get(int x,int y){ 10 if(y<=0) return f[0][0]; 11 if(y>m) return f[0][0]; 12 return f[x][y]; 13 } 14 15 int main(){ 16 scanf("%d%d%d",&n,&m,&k); 17 for(int i=0;i<n;i++) scanf("%d%d",x+i,y+i); 18 for(int i=1;i<=k;i++){ 19 int p,l,h; scanf("%d%d%d",&p,&l,&h); 20 for(int ii=h;ii<=m;ii++) b[p][ii]=i; 21 for(int ii=0;ii<=l;ii++) b[p][ii]=i; 22 } 23 memset(f,1,sizeof(f)); 24 for(int i=1;i<=m;i++) f[0][i]=0; 25 for(int i=1;i<=n;i++){ 26 for(int j=1;j<=m;j++){ 27 f[i][j]=min(f[i][j],get(i-1,j-x[i-1])+1); 28 } 29 for(int j=1;j<=m;j++) 30 f[i][j]=min(f[i][j],get(i,j-x[i-1])+1); 31 for(int j=m;j>=m-x[i-1];j--){ 32 f[i][m]=min(f[i][m],f[i-1][j]+1); 33 f[i][m]=min(f[i][m],f[i][j]+1); 34 } 35 36 for(int j=1;j<=m;j++) 37 f[i][j]=min(f[i][j],get(i-1,j+y[i-1])); 38 for(int j=1;j<=m;j++) if(b[i][j]) 39 f[i][j]=f[0][0]; 40 } 41 int minn=f[0][0]; 42 for(int i=1;i<=m;i++) minn=min(minn,f[n][i]); 43 if(minn!=f[0][0]){ 44 printf("1 %d ",minn); 45 return 0; 46 } 47 for(int i=n-1;i>=0;i--){ 48 for(int j=1;j<=m;j++) if(f[i][j]!=f[0][0]){ 49 int ans=0; 50 for(int ii=1;ii<=i;ii++) if(b[ii][m]) ans++; 51 printf("0 %d ",ans); 52 return 0; 53 } 54 } 55 }