【题解】洛谷P1941 飞扬的小鸟(dp)

Flappy Kotori是很好玩的游戏

对于这道题目,我的第一感觉是动态规划,但不知道应该如何实现。但实际上还是能想到的,只是要注意许多细节。

我们定义二维数组dp[i][j]代表到第i列第j个位置(如原题图)需要至少多少步。记录下每一列上升下降的格数up[i],down[i]。并初始化每一列能走的上限high[i]=0,下限low[i]=m+1。读入有油管的情况,将该列的上限和下限数组更新,并记录当前列有油管(便于最后无法通过的情况记录通过的油管数时使用)。初始化dp数组为INF,并初始化第0列所有行是0(一开始从第0列任意点出发)。然后我们可以从第0列到第n-1列开始,首先更新dp数组跳一下的情况(01背包),注意要判断在跳一下后如果没到天花板最高高度就是当前的+该列向上跳的高度,如果到了天花板最高高度只能是m。然后我们需要处理在单位时间内连续跳的情况(完全背包),我们这里枚举高度j 从1到m,并不断更新我们求出跳一次的值。这里可能不太好理解,举个例子,第一次跳小鸟从1跳到3,然后我们j变为2就是从2到4,然后变为3就是从3到5,这时我们可以把它看做从第一次的1跳两次到了5,。最后我们处理下落的情况,注意下落时的最低高度应该是max(最低油管的顶部+1,当前列下落距离+1),不然鸟就摔死了。然后dp更新即可,这里就不需要加步数了。初始化最小值minn为INF,最后我们对第n列的每一行dp值找最小值,如果不是INF,就输出1与minn。如果没有最小值说明飞不到那里。这时我们需要从n-1列到第1列寻找该列里能到达的每一行dp值的最小值,首先判断该行有没有油管,在这个判断前提下如果是INF说明鸟飞不到那里,所以k值减1。若不是INF就飞到那里停止了,输出0与k即可。最后注意可能有不经过任何油管但飞不到的情况,应该在这个循环外输出0,0。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=10010;
const int maxm=1010;
int dp[maxn][maxm];
int up[maxn],down[maxn],low[maxn],high[maxn];
int n,m,k;
bool b[maxn];
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i=0;i<n;i++)
	{
		scanf("%d%d",&up[i],&down[i]);
	}
	for(int i=0;i<=n;i++)
	{
		low[i]=0;
		high[i]=m+1;
	}
	for(int i=1;i<=k;i++)
	{
		int t;
		scanf("%d",&t);
		scanf("%d %d",&low[t],&high[t]);
		b[t]=true;
	}
	memset(dp,INF,sizeof(dp));
	for(int i=1;i<=m;i++) dp[0][i]=0;
	for(int i=0;i<n;i++)
	{
		for(int j=low[i]+1;j<high[i];j++)
		{
			if(j+up[i]<=m) dp[i+1][j+up[i]]=min(dp[i+1][j+up[i]],dp[i][j]+1);
			else dp[i+1][m]=min(dp[i+1][m],dp[i][j]+1);
		}
		for(int j=1;j<=m;j++)
		{
			if(j+up[i]<=m) dp[i+1][j+up[i]]=min(dp[i+1][j+up[i]],dp[i+1][j]+1);
			else dp[i+1][m]=min(dp[i+1][m],dp[i+1][j]+1);
		}
		for(int j=max(low[i]+1,down[i]+1);j<high[i];j++)
		{
			dp[i+1][j-down[i]]=min(dp[i+1][j-down[i]],dp[i][j]);
		}
	}

	int minn=INF;
	for(int i=1;i<=m;i++)
	{
		//cout<<dp[n][i]<<endl;
		minn=min(minn,dp[n][i]);
	}
	if(minn!=INF)
	{
		cout<<"1"<<endl<<minn<<endl;
		return 0;
	}
	
	for(int i=n-1;i>0;i--)
	{
		if(b[i])
		{
			int minn=INF;
			for(int j=low[i]+1;j<=high[i]-1;j++)
			{
				minn=min(minn,dp[i][j]); 
			}
			if(minn!=INF)
			{
				cout<<"0"<<endl<<k<<endl;
				return 0;
			}
			k--;
		}
		
	}
	cout<<"0"<<endl<<"0"<<endl; 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Rem_Inory/article/details/81485455