很显然的DP
我们设 f [ i ] [ j ] 表示在位置 i , j 时需要的最少的点击次数
考虑不点击的影响 f [ i ] [ j ] = f [ i-1 ] [ j + y [ i ] ]
如果点击 f [ i ] [ j ] = f [ i-1 ] [ j - x [ i ] ] +1 ,但是由于同一时间内可以点击多次,所以 f [ i ] [ j ] 可以从同一横坐标的位置转移过来
即 f [ i ] [ j ] = min(f [ i-1 ] [ j - x [ i ] ],f [ i ] [ j - x [ i ] ])+1(for循环时 j 从小到大)
注意题目的一个小坑
到最上面不会死也不会再上升
所以要特殊考虑
一定要注意不能直接把管子的位置判死,因为这个一直WA
可能上一步点击一次下一步会到管道,但是再点一次就不会碰到管道
因为我们转移有从同一横坐标转移(表示点了多次),所以先全部转完了再把管道位置的值弄成INF
大概就这样,具体实现看代码..
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=10007,M=1007,INF=2e9+7; int u[N],d[N],a[N],b[N],n,m,t;//u[i],d[i]分别表示横坐标i的管道的上下部分,a[i],b[i]表示横坐标i的上升下降距离 int f[N][M],ans; int main() { memset(f,0x7f,sizeof(f)); int pos; n=read(); m=read(); t=read(); for(int i=1;i<=n;i++) a[i]=read(),b[i]=read(),u[i]=m+1; for(int i=1;i<=t;i++) pos=read(),d[pos]=read(),u[pos]=read(); pos=0; bool flag; for(int i=1;i<=m;i++) f[0][i]=0;//一开始可以在任何位置 for(int i=1;i<=n;i++) { for(int j=1;j<u[i];j++) if(j-a[i]>0) f[i][j]=min(f[i][j] ,min(f[i-1][j-a[i]]+1,f[i][j-a[i]]+1) );//考虑上一位置多次点击的转移 if(u[i]>m) for(int j=m-a[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=d[i]+1;j<u[i];j++) if(j+b[i]<=m) f[i][j]=min(f[i][j],f[i-1][j+b[i]]);//考虑上一位置不点击的转移 for(int j=1;j<=d[i];j++) f[i][j]=f[0][0];//先转完再把管道位置弄成INF flag=0; for(int j=1;j<=m;j++) if(f[i][j]<f[0][0]) flag=1; if(!flag) { pos=i; break; }//判断能否到达终点,如果当前横坐标任意位置都无法到达说明最多到此处 } if(pos)//如果无法到达 { ans=0; for(int i=1;i<pos;i++) if(d[i]||u[i]<=m) ans++;//计算经过多少管道 printf("0 %d",ans); return 0; } //否则 ans=INF; for(int i=1;i<=m;i++) ans=min(ans,f[n][i]);//计算最少步数 printf("1 %d",ans); return 0; }