P1941 飞扬的小鸟
一、杂言
我这个题解是按照从0开始的思路来讲的,比较适合于萌新,可能有些废话,所以请各位大犇们手下留情qwq
二、引入
这个题确实是细节题,毕竟是Noip提高组题还是非常不错的,有一些细节我在这列出来:(下面描述三中的"二中O"就是指这里面的注意点)
①高度为
②可以多次点击,效果累加【因为无视了这句话写错了很多次QWQ
③管道不是按照顺序输入
④
⑤管道输入的意思是下管道的上边界L与上管道的下边界H,也就是说这个两个管道分别占地为
三、思路
这个题给出的状态有很多,那么很明显可以使用动态规划,可以列出的状态有:坐标
1、状态确定&&转移方程(可以套模板)
假设没有管道,不妨设
由题设
那么当
及要么从上一个点
当
及要么点,要么不点,而且
很明显每次选取都是在
但是有一个要求,高度为
上述
2、探究原题条件
现在又引入了一个叫做管道的鬼东西,那么我们需要再加一个特判。
如果这个地方是管道,也就是说这里不可以通过,那么就使这里的
for(int y=0;y<=low[cnt];y++)
dp[x][y]=inf;
for(int y=high[cnt];y<=m;y++)
dp[x][y]=inf;
3、初始化
其实这个应该在状态转移方程里写的,但是我还是单独拉出来了 (其实是作者突然想起来还有初始化,懒得往上插了qwq)
memset(dp,inf,sizeof(dp));
for(int y=1;y<=m;y++)
dp[0][y]=0;
三句话,没了。不过要注意的是
4、判断何时撞墙和最终答案如何输出
很明显,撞的一定是管道上(因为
为什么是
如果程序顺利通过的话,那么就直接再次寻找
5、更多优化&&部分代码细节
①、因为状态
相比之前的
for(int y=0;y<=m;j++)
dp[x%2][y]=inf;
仅仅只是在原来
②、因为题目已经说明边界为你觉得计算一个减法能需要多长时间!))
③、注意开
④、因为初始化
⑤、剩下的细节请看下面的代码,不过代码的部分数组与上述描述的名字不一样(上面的名字是我为了方便讲解取得,下面代码懒得改了qwq)
6、code (你觉得直接抄可以过吗?qwq):
#include<bits/stdc++.h>
using namespace std;
#define ll int
#define gc(a) a=getchar()
#define pc(a) putchar(a)
ll read(){
char c;ll x=0;bool flag=0;gc(c);
while(c<'0'||c>'9'){if(c=='-') flag=1;gc(c);}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48),gc(c);}
return flag?-x:x;
}
void pr(ll x){
if(x<0){x=-x;pc('-');}
if(x>9) pr(x/10);
pc(x%10+48);
}
//-------快读------
#define inf 0x3f3f3f3f
const ll maxn=10005;
const ll maxm=10005;
struct node
{
ll id,h,l;
bool operator <(const node &a) const
{
return id<a.id;
}
}o[maxn];
ll x[maxn],y[maxn],dp[2][maxm],n,m,k,cnt=1,ans;
int main()
{
//memset(dp,inf,sizeof(dp));//两个被遗忘的初始化之一qwq
n=read(),m=read(),k=read();
for(int i=1;i<=n;i++)
x[i]=read(),y[i]=read();
for(int i=1;i<=k;i++)
o[i].id=read(),o[i].l=read(),o[i].h=read();
sort(o+1,o+k+1);//管道id排序!
//for(int i=1;i<=m;i++)
//dp[0][i]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)//注意要初始化!
dp[i%2][j]=inf;
for(int j=x[i]+1;j<=x[i]+m;j++)//p=1,完全背包
dp[i%2][j]=min(dp[i%2^1][j-x[i]]+1,dp[i%2][j-x[i]]+1);
for(int j=m+1;j<=x[i]+m;j++)//比m大的都是m
dp[i%2][m]=min(dp[i%2][m],dp[i%2][j]);
for(int j=1;j<=m-y[i];j++)//p=0,01背包
dp[i%2][j]=min(dp[i%2][j],dp[i%2^1][j+y[i]]);
if(i==o[cnt].id)//如果这个地方有管道
{
ans=inf;//主要每次都要初始化一次!
for(int j=0;j<=o[cnt].l;j++)
dp[i%2][j]=inf;
for(int j=o[cnt].h;j<=m;j++)
dp[i%2][j]=inf;
for(int j=1;j<=m;j++)//寻找是否可以通过
ans=min(dp[i%2][j],ans);
if(ans==inf)
{
pr(0);pc('\n');pr(cnt-1);return 0;
}
cnt++;
}
}
ans=inf;//注意要初始化!
for(int j=1;j<=m;j++)
ans=min(dp[n%2][j],ans);
pr(1);pc('\n');pr(ans);
return 0v0;
}
4、后语
题解千万条,细节第一条
题解没有赞,作者两行泪
你看作者因为写题解都把上面的数字记成4了,你们忍心咩qwq