【NOIP2014】飞扬的小鸟
设 f[i][j]表示在坐标(i,j)点时的最小点击屏幕次数。
DP方程为 f[i][j]=min(f[i-1][j-x[i-1]]+1,f[i][j-x[i-1]]+1,f[i-1][j+y[i-1]]);
分别表示点击一次,点击多次和下降的情况。
实现方案如下:
首先全部赋最大值
然后处理一次点击和连续点击,注意到了上界需要特殊的搞一下
再处理下落的情况
处理完后记得把柱子的f赋为最大值
然后最后扫一遍判断答案
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<queue> #include<cctype> #include<cfloat> #include<vector> #include<map> using namespace std; const int maxn=10000+10; const int maxm=1000+10; const int INF=100000000; int f[maxn][maxm]; int x[maxn],y[maxn],down[maxn],up[maxn]; int main() { freopen("!.in","r",stdin); freopen("!.out","w",stdout); int n,m,a; cin>>n>>m>>a; for(int i=0;i<n;i++) scanf("%d%d",&x[i],&y[i]); for(int i=1;i<=n;i++) {down[i]=0;up[i]=m+1;} int p,l,h; for(int i=0;i<a;i++){ scanf("%d%d%d",&p,&l,&h); down[p]=l;up[p]=h; } 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); } for(int j=m-x[i-1];j<=m;j++) { f[i][m]=min(f[i][m],f[i-1][j]+1); f[i][m]=min(f[i][m],f[i][j]+1); } for(int j=down[i]+1;j<=up[i]-1;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<=down[i];j++) f[i][j]=INF; for(int j=up[i];j<=m;j++) f[i][j]=INF; } int ans=INF,num=a; for(int i=n;i>=1;i--) { for(int j=down[i]+1;j<up[i];j++) ans=min(ans,f[i][j]); if(ans!=INF) break; if(up[i]<=m) num--; } if(num==a) printf("1\n%d",ans); else printf("0\n%d",num); return 0; }