题意:
Flappy Bird是一款风靡一时的休闲手机游戏。(笨鸟瞎jb飞︿( ̄︶ ̄)︿)
游戏界面是一个长为 n,高为 m 的二维平面,
其中有 k 个管道(忽略管道的宽度)。
小鸟始终在游戏界面内移动。
小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。
小鸟每个单位时间沿横坐标方向右移的距离为 1,竖直移动的距离由玩家控制。
如果点击屏幕,小鸟就会上升一定高度 X,每个单位时间可以点击多次,效果叠加;
如果不点击屏幕,小鸟就会下降一定高度 Y。
小鸟位于横坐标方向不同位置时,上升的高度 X 和下降的高度 Y 可能互不相同。
小鸟高度等于 0 或者小鸟碰到管道时,游戏失败。
小鸟高度为 m 时,无法再上升。
现在,请你判断是否可以完成游戏。如果可以,输出最少点击屏幕数;
否则,输出小鸟最多可以通过多少个管道缝隙。
爆搜大法好。。。。。45
#include<iostream> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define int long long #define olinr return #define _ 0 #define love_nmr 0 #define DB double struct node { int up; int down; }; node h[10505]; struct wmy { int top; int low; int x; }; wmy limit[10505]; int n; int m; bool flag; int k; int tot; int ans=0x7fffffff; inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-f; ch=getchar(); } while(isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } inline void put(int x) { if(x<0) { x=-x; putchar('-'); } if(x>9) put(x/10); putchar(x%10+'0'); } inline void dfs(int high,int x,int step,int num) { if(x==n) { flag=true; ans=min(ans,step); return; } if(x==limit[num].x) { tot=max(tot,num); if(high<=limit[num].low||high>=limit[num].top) return; if(high>h[x].down) dfs(high-h[x].down,x+1,step,num+1); for(int i=1;;i++) { if((i-1)*h[x].up+high>m) break; dfs(min(m,high+i*h[x].up),x+1,step+i,num+1); } } else { if(high>h[x].down) dfs(high-h[x].down,x+1,step,num); for(int i=1;;i++) { if((i-1)*h[x].up+high>m) break; dfs(min(m,high+i*h[x].up),x+1,step+i,num); } } } signed main() { n=read(); m=read(); k=read(); for(int i=0;i<n;i++) { h[i].up=read(); h[i].down=read(); } limit[1].x=-1; for(int i=1;i<=k;i++) { limit[i].x=read(); limit[i].low=read(); limit[i].top=read(); } for(int i=0;i<=m;i++) dfs(i,0,0,1); if(flag) { putchar('1'); putchar('\n'); put(ans); } else { putchar('0'); putchar('\n'); put(tot); } olinr ~~(0^_^0)+love_nmr; }
然而,正解居然是背包DP(蒟蒻瑟瑟发抖。。。)
以f[i][j]代表飞到i,j时用的最少点击次数
那么如果向上飞,则$f[i][j]=min(f[i][j],f[i-1][j-x[i]*p]+p)$ p为点击次数(枚举)
然而这样会T
仔细观察转移方程
这。。。。。。这TM是完全背包啊QAQ
不过要用二维转移
$f[i][j]=min(f[i-1][j-x[i]]+1,f[i][j-x[i]]+1), j \ \epsilon\ [x[i]+1,m+x[i]] $
注意要到$x[i]+m$因为跳的时候可能超过m
不过超过m的部分要给它压回去,相当于在离m很近时跳一次($<x[i]$的距离)到m
对于自然掉落
类比一下
01背包呗(注意:这里是二维,不用倒序枚举)
对于不合法的边界,再最后重新赋为初始的极大值(千万别忘,因为上面的转移不考虑管道限制)
#include<cstdio> #include<iostream> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define olinr return #define _ 0 #define love_nmr 0 #define DB double struct node { int up; int down; }; node h[10505]; struct wmy { int top; int low; }; wmy limit[10505]; int n; int m; bool flag; int k; int tot; int ans; int f[10505][2050]; bool have[10505]; inline int read() { int x=0,f=1; char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-f; ch=getchar(); } while(isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } inline void put(int x) { if(x<0) { x=-x; putchar('-'); } if(x>9) put(x/10); putchar(x%10+'0'); } int main() { n=read(); m=read(); k=read(); for(int i=1;i<=n;i++) { h[i].up=read(); h[i].down=read(); limit[i].top=m; limit[i].low=1; } limit[n].top=m; limit[n].low=1; for(int x,i=1;i<=k;i++) { x=read(); limit[x].low=read()+1; limit[x].top=read()-1; have[x]=true; } memset(f,0x3f,sizeof f); for(int i=1;i<=m;i++) f[0][i]=0; for(int i=1;i<=n;i++) { for(int j=h[i].up+1;j<=h[i].up+m;j++) //向上完全背包 f[i][j]=min(f[i-1][j-h[i].up]+1,f[i][j-h[i].up]+1); for(int j=m+1;j<=m+h[i].up;j++) f[i][m]=min(f[i][m],f[i][j]); //超过 m 的部分 for(int j=1;j<=m-h[i].down;j++) //向下01背包 f[i][j]=min(f[i][j],f[i-1][j+h[i].down]); for(int j=1;j<limit[i].low;j++) //不合法的 f[i][j]=f[0][0]; for(int j=limit[i].top+1;j<=m;j++) f[i][j]=f[0][0]; } ans=f[0][0]; for(int i=1;i<=m;i++) ans=min(ans,f[n][i]); if(ans<f[0][0]) { putchar('1'); putchar('\n'); put(ans); } else { int pos=0; for(int i=n;i>=1;i--,pos=i) { for(int j=1;j<=m;j++) { if(f[i][j]<f[0][0]) goto shit; } } shit:; ans=0; for(int i=1;i<=pos;i++) if(have[i]) ans++; put(0); putchar('\n'); put(ans); } olinr ~~(0^_^0)+love_nmr; }