1 条题解
-
0
自动搬运
来自洛谷,原作者为

王sky
**搬运于
2025-08-24 21:25:08,当前版本为作者最后更新于2019-10-25 17:57:21,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这题应该不难想到使用dp求解。
我们先将每块木板按照高度排序。 然后我们按照从矮到高的顺序倒序遍历这些木板。 可能你会问为什么要从矮到高的顺序dp。 其实从高到到矮的顺序dp,其实从高到矮并不是不可以。 但从低到高会来的更方便。 因为从低到高的话,我们只需要考虑从左边下落到达的木板,和从右边下落到达的两个木板继承就可以了。 然而从高到低我们并不方便找到哪些下落可以到达该木板。
以上都是废话假若我们已经求得了这个木板左右下落会到达哪些木板, 我们记 ch[i][1/0] 0表示i的左边下落到达的木板,1表示右边下落到达的木板。
dp[i][1/0] 0表示如果下落到左边木板,最小费时,1表示下落到右边木板的最小费时。
la[i] 表示 i 的右端点横坐标, ra[i] 表示 i 的左端点横坐标。
a[i].h 记录 i 木板的高度。
dp 方程如下:
dp[i][0]=min(dp[i][0],dp[ch[i][0]][0]+la[i]-la[ch[i][0]]+a[i].h-a[ch[i][0]].h)
dp[i][0]=min(dp[i][0],dp[ch[i][0]][1]+ra[ch[i][0]]-la[i]+a[i].h-a[ch[i][0]].h);
dp[i][1]=min(dp[i][1],dp[ch[i][1]][0]+ra[i]-la[ch[i][1]]+a[i].h-a[ch[i][1]].h);
dp[i][1]=min(dp[i][1],dp[ch[i][1]][1]+ra[ch[i][1]]-ra[i]+a[i].h-a[ch[i][1]].h);
其实这后面转移就是路径的模拟,不难理解。
dp方程解决了,还有一个问题,那就是如何处理出每个木板左右下落后到达的木板?
一个比较好理解的方案是用线段树预处理。
这里使用了区间赋值和单点修改。
我们从低到高枚举木板,查询左/右端点下线段树上对应的值。 这值就是我们要求的是落到的那个木板编号。 当处理完这个木板完后,他就是对应区间(木板的左端点到右端点),最上面的木板了。 它上面的应当会落在它的上面。 于是区间修改。把这段区间的值修改为这个木板的编号。
到此,问题得到完美解决等等,我们千万不要忘记离散化,以及dp时特判。 如果当前的木板下落到的木板编号为0,那么dp值是当前木板的高度。
具体看代码
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ls k<<1 #define rs k<<1|1 typedef long long ll; using namespace std; const int N=1e6; ll n,m,cnt; ll lx[N<<2],ch[N][2],w[N<<4],tag[N<<4];// ll la[N<<2],ra[N<<2],dp[N][2];// struct node{ ll l,r,h,id;//需要记录一个编号,因为需要排序 bool operator < (const node &b) const { return h<b.h;//按高度从矮到高排序 } }a[N]; inline void read(ll &x){ x=0; char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); } inline void update(ll k,ll x,ll y){ tag[ls]=tag[k]; w[ls]=tag[k]; tag[rs]=tag[k]; w[rs]=tag[k]; tag[k]=0; } inline void change(ll k,ll x,ll y,ll l,ll r,ll val){ if(x>r||y<l) return; if(x>=l&&y<=r){//区间赋值 tag[k]=val; w[k]=val; return; } ll mid=x+y>>1; if(tag[k]) update(k,x,y);//区间赋值的标记下传 change(ls,x,mid,l,r,val); change(rs,mid+1,y,l,r,val); } inline ll query(ll k,ll x,ll y,ll pos){ if(x>pos||y<pos) return 0; if(x==y) return w[k];//单点查询,w记录当前区间对应最上方的木板编号 ll mid=x+y>>1; if(tag[k]) update(k,x,y); return query(ls,x,mid,pos)+query(rs,mid+1,y,pos); } int main() { ll i,j,stx,sty; read(n),read(m); read(stx),read(sty); n++;//有一个起始位置 a[n].l=a[n].r=stx; a[n].h=sty,a[n].id=n; la[n]=stx,ra[n]=stx; lx[++cnt]=a[n].l; for(i=1;i<n;i++){ read(a[i].h),read(a[i].l),read(a[i].r); a[i].id=i; la[i]=a[i].l; ra[i]=a[i].r;//la[] ra[]记录原来i木板的左右端点位置,因为需要离散化。 lx[++cnt]=a[i].l;//lx[] 为离散化数组 lx[++cnt]=a[i].r; } sort(lx+1,lx+cnt+1);//离散化数组排序 sort(a+1,a+1+n);//木板高度排序 for(i=1;i<=n;i++){//区间离散化 a[i].l=lower_bound(lx+1,lx+1+cnt,a[i].l)-lx; a[i].r=lower_bound(lx+1,lx+1+cnt,a[i].r)-lx; } for(i=1;i<=n;i++){//点查询,不再多说 ch[i][0]=query(1,1,cnt,a[i].l); ch[i][1]=query(1,1,cnt,a[i].r); change(1,1,cnt,a[i].l,a[i].r,i); } memset(dp,0x3f,sizeof(dp)); dp[0][0]=dp[0][1]=0;//显而易见的初始化 for(i=1;i<=n;i++){ if(a[i].h-a[ch[i][0]].h<=m){//如果大于m不能转移 if(ch[i][0]){//编号为0这带到到达地面 dp[i][0]=min(dp[i][0],dp[ch[i][0]][0]+la[a[i].id]-la[a[ch[i][0]].id]+a[i].h-a[ch[i][0]].h); dp[i][0]=min(dp[i][0],dp[ch[i][0]][1]+ra[a[ch[i][0]].id]-la[a[i].id]+a[i].h-a[ch[i][0]].h);//加了离散数组的方程,一点点小变化 } else dp[i][0]=a[i].h; } if(a[i].h-a[ch[i][1]].h<=m){ if(ch[i][1]){ dp[i][1]=min(dp[i][1],dp[ch[i][1]][0]+ra[a[i].id]-la[a[ch[i][1]].id]+a[i].h-a[ch[i][1]].h); dp[i][1]=min(dp[i][1],dp[ch[i][1]][1]+ra[a[ch[i][1]].id]-ra[a[i].id]+a[i].h-a[ch[i][1]].h); } else dp[i][1]=a[i].h; } } printf("%d",min(dp[n][0],dp[n][1]));//最后答案为转移到起始位置 n 的值。 }//刚好99行
- 1
信息
- ID
- 436
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者