NOIP 2014 飞扬的小鸟
2017-10-01 17:24
239 查看
洛谷 P1941 飞扬的小鸟
【题目分析】
首先想到设f[i][j]表示到第i行第j列所需要的最少点击屏幕次数。转移方程为f[ i ][ j ]=min{f[ i-1 ][ j - k*x[i-1] ] + k} (1<= k <= j/x) 上升——①
f[ i ][ j ]=min{f[ i-1 ][ j + y[i-1] } ( j + y[i-1] <= m) 下降
显然,下降可以O(1)转移,主要问题在上升的转移。
我们将上升的方程变一下:
f[ i ][ j-x[i-1]]=min{f[i-1][(j-x[i-1])-(k-1)*x[i-1]]+k-1} ——②
这是 f[ i ][ j - x[i-1] ] 的转移。
由 ② 化简可得:
f[ i ][ j - x[i-1] ]=min{f[ i-1 ][ j - k*x[ i-1] ] + k -1}——③
由①③消去f[ i-1 ][ j - k*x[ i-1] ]+k可得
f[ i ][ j ]= f[ i ][ j - x[ i-1 ] ]+1
于是就可以O(n*m)的时间内出解
以上分析来自Y142857
这个说的也还可以:【NOIP2014】飞扬的小鸟题解
【程序代码】
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; const int inf=0x7ffffff; int n,m,k,x[10010],y[10010],down[10010],up[10010],f[10010][1001]; inline int read() { int x=0,w=1; char ch=0; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') w=-1,ch=getchar(); while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar(); return x*w; } int main() { n=read(); m=read(); k=read(); for(int i=0; i<n; i++) x[i]=read(),y[i]=read();//从0到n-1 for(int i=1; i<=n; i++) {//先把down和up初始化一下,上街和下街 down[i]=0; up[i]=m+1; } for(int i=1; i<=k; i++) {//这里是把有关管道的限制给赋值上去 int p=read(),l=read(),h=read(); down[p]=l; up[p]=h; } for(int i=1; i<=n; i++)//初始化,因为一开始读入的时候是从0开始的,所以这里要从1开始,不然的话就会错误 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);//这里是完全背包,具体见博客解释 } if(j==m) {//如果相等的话,得特殊处理一下 for(int k=j-x[i-1]; k<=m; k++) {//从j-x[i-1]到m这一段是需要更新的,也就是加上之后会跑到边界之上去,而实际上不会 f[i][j]=min(f[i][j],f[i-1][k]+1); f[i][j]=min(f[i][j],f[i][k]+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]]);//处理向下掉的情况,每次只能向下掉一次,所以是01背包 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 cut=k,ans=inf; for(int i=n; i>=1; i--) { for(int j=down[i]+1; j<=up[i]-1; j++) if(f[i][j]<inf) ans=min(ans,f[i][j]);//在限定的范围内寻找一个最优解 if(ans!=inf) break;//当这个跳得过去了,则可以退出了这一层循环,以至于cut不会有所减少 if(up[i]<=m) cut--;//这个仔细想想应该就知道了 } if(cut==k)printf("1\n%d\n",ans); else printf("0\n%d\n",cut); return 0; }
小结:DP真的有蛮难啊,还得去练习更多的题啊,不过很多次自己都想不出啊,求大佬帮助。。。。。。
相关文章推荐
- NOIP 2014 Day1 T3飞扬的小鸟
- 【NOIP2014】 飞扬的小鸟 完全背包dp
- [NOIP2014] 提高组 洛谷P1941 飞扬的小鸟
- 【NOIP2014 Day1 T3】飞扬的小鸟
- 【NOIP2014】飞扬的小鸟
- NOIP2014飞扬的小鸟
- 飞扬的小鸟 noip2014
- 【NOIp 2014】【二维dp】飞扬的小鸟
- noip提高组2014 飞扬的小鸟
- NOIP 2014 Day1 T3飞扬的小鸟
- LuoguP1941[NOIP2014] 飞扬的小鸟 解题报告【背包型DP】
- NOIP 2014 飞扬的小鸟
- [noip2014tg] 飞扬的小鸟
- 【洛谷2439】【NOIP2014】飞扬的小鸟
- 【NOIP 2014 day1 T3】飞扬的小鸟——题解
- [NOIP2014]飞扬的小鸟
- 【NOIP2014】飞扬的小鸟
- NOIP2014 飞扬的小鸟
- 【NOIP2014提高组T3】飞扬的小鸟-完全背包
- [NOIp2014] 飞扬的小鸟