【NOIP2014】飞扬的小鸟
题解:首先设置一个状态dp[i][j]表示到达位置i高度为j的最小点击次数。
我们讨论一下:
在i-1为位置跳的情况----
(1)
cap[i][j]=min(cap[i][j],cap[i-1][j-up[i-1]]+1);//由i-1位置跳一次而来
cap[i][j]=min(cap[i][j],cap[i][j-up[i-1]]+1);//可以解决多次点击的叠加效果(自己理解一下)
(2)
for(int p=j-up[i-1];p<=m;p++)//当当前的高度为m时,显然可以由i-1位置的高度从j-up[i-1]到m到的状态转移过来
{
cap[i][j]=min(cap[i][j],cap[i-1][p]+1);
cap[i][j]=min(cap[i][j],cap[i][p]+1);
}
下落的情况------
for(int j=L[i]+1;j<=H[i]-1;j++)
{
if(j+down[i-1]<=m)
cap[i][j]=min(cap[i][j],cap[i-1][j+down[i-1]]);
}
以下是代码。
#include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<algorithm> #include<vector> #include<set> #include<cstring> #include<queue> #include<stack> #define MAXN 10010 #define LL long long int using namespace std; const int INF=1e9; const int EPS=1e-8; struct node{ int l; int h; int w; }t[MAXN]; int cap[10001][1001];//到达横坐标为i高度为j的地方的最小步数 int n,m,k; int w[MAXN]; int L[MAXN],H[MAXN]; int up[MAXN],down[MAXN]; int main() { freopen("1.in","r",stdin); freopen("1.out","w",stdout); scanf("%d%d%d",&n,&m,&k); for(int i=0;i<=n-1;i++) scanf("%d%d",&up[i],&down[i]); for(int i=0;i<=n;i++) L[i]=0,H[i]=m+1; int p,l,h; for(int i=1;i<=k;i++) { scanf("%d%d%d",&p,&l,&h); t[i].w=p;t[i].l=l;t[i].h=h; w[p]=1; L[p]=l; H[p]=h; } for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) cap[i][j]=INF; cap[0][0]=INF; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(j>=up[i-1]) { cap[i][j]=min(cap[i][j],cap[i-1][j-up[i-1]]+1); cap[i][j]=min(cap[i][j],cap[i][j-up[i-1]]+1); } if(j==m) { for(int p=j-up[i-1];p<=m;p++) { cap[i][j]=min(cap[i][j],cap[i-1][p]+1); cap[i][j]=min(cap[i][j],cap[i][p]+1); } } } for(int j=L[i]+1;j<=H[i]-1;j++) { if(j+down[i-1]<=m) cap[i][j]=min(cap[i][j],cap[i-1][j+down[i-1]]); } for(int j=1;j<=L[i];j++) cap[i][j]=INF; for(int j=H[i];j<=m;j++) cap[i][j]=INF; } int ans=INF,tot=k; for(int i=n;i>=1;i--) { for(int j=L[i]+1;j<=H[i]-1;j++) if(cap[i][j]<INF) ans=min(ans,cap[i][j]); if(ans!=INF) break; if(w[i]==1) tot--; } if(tot==k) { cout<<1<<endl; cout<<ans<<endl; } else { cout<<0<<endl; cout<<tot<<endl; } return 0; }