您的位置:首页 > 其它

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真的有蛮难啊,还得去练习更多的题啊,不过很多次自己都想不出啊,求大佬帮助。。。。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: