NOIP 2014 飞扬的小鸟

     一个DP题,我们考虑一个状态,F[i][j]表示到i这个横坐标,当前高度为j所需的最少点击次数。那么我们有F[i][j]的方程为F[i][j]=min{F[i-1][j+y[i]],F[i-1][j-k*x[i]]+k},这样做的复杂度是O(n*m*k)的,我们考虑优化,这个状态是不可能再优化的了,那我们考虑优化转移,我们发现其实k的枚举是有很多重复的,我们可以将转移方程改写为F[i][j]=min{F[i-1][j+y[i]],F[i][j-x[i]]+1,F[i-1][j-x[i]]+1},这样只要我们能保证转移顺序是按照j递增的,就可以省去k的枚举,这题有很多细节,比如说如果选择下降就不能再点,这就要求我们在考虑完点屏幕后再考虑下降,还有对于管道的处理。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 10005
struct Vergil
{
	int L,H,P;
}t[maxn];
int n,m,w,p=1,ans;
int x[maxn],y[maxn],f[maxn][1005];

bool cmp(Vergil a,Vergil b) 
{
	return a.P<b.P;	
}

int main()
{
	scanf("%d%d%d",&n,&m,&w);
	for (int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
	for (int i=1;i<=w;i++) 
		scanf("%d%d%d",&t[i].P,&t[i].L,&t[i].H);
	sort(t+1,t+w+1,cmp);
	memset(f,53,sizeof f);
	for (int i=1;i<=m;i++) f[0][i]=0;
	for (int i=1;i<=n;i++) 
	{
		if (t[p].P==i) 
		{
			bool ok=0;
			for (int j=1;j<t[p].H;j++) 
			{
				if (j-x[i]>0) 
					f[i][j]=min(f[i][j],min(f[i-1][j-x[i]],f[i][j-x[i]])+1);	
			}
			for (int j=1;j<t[p].H;j++) 
				if (j+y[i]<=m&&j>t[p].L) f[i][j]=min(f[i][j],f[i-1][j+y[i]]);	
			for (int j=1;j<t[p].H;j++) 
			{
				if (j<=t[p].L) f[i][j]=1e8;
				if (f[i][j]<1e7) ok=1;	
			}
			if (!ok) 
			{
				printf("0\n%d\n",p-1);
				return 0;	
			}
			p++;
		}
		else 
		{
			for (int j=1;j<=m;j++) 
			{
				if (j-x[i]>0) 
					f[i][j]=min(f[i][j],min(f[i-1][j-x[i]],f[i][j-x[i]])+1);
			}
			for (int j=1;j<=m;j++) 
				if (j+y[i]<=m) f[i][j]=min(f[i-1][j+y[i]],f[i][j]);;
			for (int j=m-x[i];j<=m;j++) 
				f[i][m]=min(f[i][m],min(f[i-1][j],f[i][j])+1);
		}
	}
	ans=f[n][1];
	for (int i=2;i<=m;i++) ans=min(ans,f[n][i]);
	printf("1\n%d\n",ans);
	return 0;	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值