【NOIP2014day1】飞扬的小鸟

题目

题解

显然想到DP,设 f [ i ] [ j ] f[i][j] f[i][j]表示到了第 i i i行,第 j j j列时的最少点击数量

转移可以从两个地方转移:点了 x x x下和从上面降下来,降下来容易处理,点了 x x x下就需要去枚举 x x x,然后转移

降下来: f [ i ] [ j ] = m i n ( f [ i − 1 ] [ j + y [ i ] ] ) f[i][j]=min(f[i-1][j+y[i]]) f[i][j]=min(f[i1][j+y[i]])注意 j + y [ i ] ≤ m j+y[i]\le m j+y[i]m

上去: f [ i ] [ j ] = m i n ( f [ i − 1 ] [ min ⁡ ( m , j − x [ i − 1 ] ∗ k ) ] ) f[i][j]=min(f[i-1][\min(m,j-x[i-1]*k)]) f[i][j]=min(f[i1][min(m,jx[i1]k)])

注意判断是否会撞到水管

但这样的时间复杂度是 O ( n m 2 ) O(nm^2) O(nm2),只能拿到 75 % 75\% 75%的数据

那么就需要优化

我们发现时间在枚举点击多少次的时候多了一层循环,那么我们考虑一下把这层循环去掉

发现上升其实是一个完全背包,那么转移就可以从两个地方转移:

f [ i ] [ j ] = m i n ( f [ i − 1 ] [ j − x [ i − 1 ] ] , f [ i ] [ j − x [ i − 1 ] ] ) + 1 f[i][j]=min(f[i-1][j-x[i-1]],f[i][j-x[i-1]])+1 f[i][j]=min(f[i1][jx[i1]],f[i][jx[i1]])+1

下降转移不变

特判一下 j = m j=m j=m的情况

Code

#include<cstdio>
#include<algorithm>
#include<cstring>
#define inf 1e9
using namespace std;
struct node
{
	int up,down;
}c[10005];
int n,m,q,pos,down,up,ans,x[10005],y[10005],num[10005],f[10005][1005];
bool g[10005][1005];
bool pd(int h,int w)
{
	if (h<=c[w].down) return false;
	if (h>=c[w].up) return false;
	return true;
}
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for (int i=0;i<n;++i)
		scanf("%d%d",&x[i],&y[i]);
	for (int i=1;i<=q;++i)
	{
		scanf("%d%d%d",&pos,&down,&up);
		c[pos].down=down;
		c[pos].up=up;
	}
	for (int i=1;i<=n;++i)
	{
		if (!c[i].up) c[i].up=m+1,num[i]=num[i-1];
		else num[i]=num[i-1]+1;
	}
	for (int i=1;i<=n;++i)
		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)
				{
					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=c[i].down+1;j<c[i].up;++j) if (j+y[i-1]<=m) f[i][j]=min(f[i][j],f[i-1][j+y[i-1]]);
		for (int j=1;j<=c[i].down;++j) f[i][j]=inf;
		for (int j=m;j>=c[i].up;--j) f[i][j]=inf;
	}
	ans=inf;
	for (int i=1;i<=m;++i)
		ans=min(ans,f[n][i]);
	if (ans==inf) 
	{
		printf("0\n");
		for (int i=n-1;i>=0;--i)
			for (int j=1;j<=m;++j)
				if (f[i][j]!=inf)
				{
					printf("%d\n",num[i]);
					return 0;
				}
	}
	else printf("1\n%d\n",ans);
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值