传送门
思路:
动态规划,设 f [ i ][ j ] 表示小鸟到达坐标( i , j ) 所要点击屏幕的最少次数,如果小鸟无法到达( i , j ),则 f [ i ][ j ] = INF。
转移,考虑小鸟上升,和下降两种状态。
因为,在一个点上升次数不限,下降只有一次。
所以上升可以用 完全背包 的状态转移,下降用 01背包 转移。
特判,当小鸟到达顶上 m 时,如果继续点击屏幕,小鸟不会上升、死亡,也不会掉下来,但是屏幕点击次数要 +1 。
Code:
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<string> #include<cstdlib> #include<stack> #include<vector> #include<queue> #include<deque> #include<map> #include<set> using namespace std; #define lck_max(a,b) ((a)>(b)?(a):(b)) #define lck_min(a,b) ((a)<(b)?(a):(b)) typedef long long LL; const int maxn=10005; const int INF=1e9+7; LL n,m,k,i,j,ans=INF; LL f[maxn][4005]; struct hh { LL x,y; }t[maxn]; struct hhh { LL low,high;bool p; }g[maxn]; inline LL read() { LL kr=1,xs=0; char ls; ls=getchar(); while(!isdigit(ls)) { if(!(ls^45)) kr=-1; ls=getchar(); } while(isdigit(ls)) { xs=(xs<<1)+(xs<<3)+(ls^48); ls=getchar(); } return xs*kr; } inline void out(LL xs) { if(!xs) {putchar(48); return;} if(xs<0) putchar('-'),xs=-xs; int kr[57],ls=0; while(xs) kr[++ls]=xs%10,xs/=10; while(ls) putchar(kr[ls]+48),ls--; } int main() { LL a,b,c; n=read();m=read();k=read(); for(i=1;i<=n;i++) t[i].x=read(),t[i].y=read(),g[i].low=1,g[i].high=m; for(i=1;i<=k;i++) { a=read();b=read();c=read(); g[a].p=true,g[a].low=b+1,g[a].high=c-1; } for(i=1;i<=n;i++) for(j=0;j<=m;j++) f[i][j]=INF; for(i=1;i<=n;i++) { for(j=t[i].x+1;j<=m+t[i].x;j++)//枚举小鸟上升 f[i][j]=lck_min(f[i-1][j-t[i].x]+1,f[i][j-t[i].x]+1); for(j=m+1;j<=m+t[i].x;j++)//枚举到顶上 f[i][m]=lck_min(f[i][m],f[i][j]); for(j=1;j<=m-t[i].y;j++)//枚举下降 f[i][j]=lck_min(f[i][j],f[i-1][j+t[i].y]); for(j=1;j<g[i].low;j++)//枚举不能过柱子的情况。 f[i][j]=INF; for(j=g[i].high+1;j<=m;j++) f[i][j]=INF; } for(i=1;i<=m;i++) ans=lck_min(ans,f[n][i]); if(ans<INF) {out(1),putchar(' '),out(ans),putchar(' ');return 0;} for(i=n;i>=1;i--) { for(j=1;j<=m;j++) if(f[i][j]<INF) break; if(j<=m) break; } for(j=1;j<=i;j++)//记录小鸟最多能过多少柱子 if(g[j].p) ans++; out(0),putchar(' '),out(ans-INF),putchar(' '); return 0; }