1. 程式人生 > >【題解】洛谷P1941 [NOIP2014TG] 飛揚的小鳥(揹包DP)

【題解】洛谷P1941 [NOIP2014TG] 飛揚的小鳥(揹包DP)

次元傳送門:洛谷P1941

思路

從題意可知 在每個單位時間內 可以無限地向上飛 但是隻能向下掉一次

所以我們可以考慮運用揹包解決這道題

上升時 用完全揹包

下降時 用01揹包

設f[x][y]為在座標(x,y)時的最小點選螢幕次數

當飛到天花板時和撞到柱子時特判

一開始設ans為極大值

如果最後一排的值都為極大值則無解

如果無解就倒著回來判斷最遠能到達第幾根柱子

程式碼

#include<iostream>
#include<cstring>
using namespace std;
#define maxn 10010
int up[maxn],down[maxn],low[maxn],high[maxn];
int f[maxn][2010]; bool vis[maxn]; int n,m,k,cnt,ans; int main() { memset(f,0x3f,sizeof(f));//初始化為極大值 cin>>n>>m>>k; for(int i=1;i<=n;i++) cin>>up[i]>>down[i]; for(int i=1;i<=n;i++)//初始化柱子 { low[i]=1; high[i]=m; }
for(int i=1;i<=k;i++) { int x,y,z; cin>>x>>y>>z; vis[x]=1; low[x]=y+1; high[x]=z-1; } for(int i=1;i<=m;i++) f[0][i]=0;//第一排全是0 for(int i=1;i<=n;i++)//列舉x座標 { for(int j=1+up[i];j<=m+up[i];j++)//完全揹包 當前點可以由前一個點上升一次而來
f[i][j]=min(f[i-1][j-up[i]]+1,f[i][j-up[i]]+1);//也可以由當前點上升一次而來 for(int j=m+1;j<=m+up[i];j++)//天花板特判 f[i][m]=min(f[i][m],f[i][j]); for(int j=1;j<=m-down[i];j++)//01揹包 當前點由前一個點掉下來 f[i][j]=min(f[i][j],f[i-1][j+down[i]]); for(int j=1;j<low[i];j++)//判斷柱子 f[i][j]=f[0][0]; //賦值為極大值 for(int j=high[i]+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]);//查詢ans if(ans<f[0][0]) cout<<1<<endl<<ans;//如果有ans else//如果無解 { int now,j; for(now=n;now>=1;now--)//倒著判斷 { for(j=1;j<=m;j++)//如果當前排可以到達就退出 if(f[now][j]<f[0][0]) break; if(j<m) break; } ans=0; for(int i=1;i<=now;i++) if(vis[i]) ans++;//計算可以到達的x座標中有幾個管子 cout<<0<<endl<<ans; } }