题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3650
题意: 给你n个骨牌,每个骨牌有一个在x轴上的位置和高度,每个骨牌可以想左推也可以向右推,问最少多少步可以把骨牌全部推倒。
思路:
之前Goodbye 2014 上的E题就是一道多米诺骨牌的题目,虽然跟这道题目不太一样但是还是提供了一下思路, 附题解的链接:
http://blog.youkuaiyun.com/u013649253/article/details/42360503
建立在之前那题题目的基础上,这道题目我还是前后做了三个小时才最后a掉,总的来说这道题目就是:细节多,码量大,转移复杂。
dp的定义:
dp[i][0] 表示把前1 ~ i个骨牌全部推倒, 且第i个向右倒的最小步数, dp[i][1]表示把前1 ~ i个骨牌全部推倒,且第i个向左倒的最小步数。
-----------
dp[i][0]的转移考虑两种情况, 一种是被它左边的骨牌推倒,第二种情况是自己用手把它推倒。
对于第一种转移情况,有dp[i][0] = min(dp[i][0], dp[j][0]) (j为满足条件 x[j] + h[j] >= x[i] && j < i ),所有满足条件的j快速的找出来并不简单, 所有我们可以换一种思路就是利用刷表法的思路,再计算出j的时候把区间x[j] ~ x[j] + h[j]上通过区间赋值都附成dp[j][0],这样在更新到dp[i][0]的时候已经满足了第一种转移情况的最小值了。
对于第二种转移情况,有dp[i][0] = min(dp[i][0], min(dp[i-1][0], dp[i-1][1]) + 1) 这就相当于取将前1~i-1个全部推倒的最小值再加1。
转移完后需要再用dp[i][0] 来更新 i ~ kr 区间上的值(kr为i骨牌向右能达到的最远的骨牌)。 此处用到的相当于是线段树的区间更新(区间赋值), 单点查询(最小值),需要维护一个域和一个懒标记。
-----------
dp[i][1]的转移也有两种情况, 记kl为第i个骨牌向左能达到的最远的骨牌的标号,则
dp[i][1] = min (dp[i][1], min(dp[kl-1][1], dp[kl-1][1]) + 1)
dp[i][1] = min (dp[i][1], dp[j][1]) ( kl <= j <= i) 此处不用+1, 虽然是用手推的但是,kl ~ i的骨牌一定是一起倒的(只用手推一次),其转移的状态一定是先经过第一种转移的,所以此处不+1,这个地方我一开始都没有注意到。
所以此处的转移,用线段树维护一个单点更新 区间查询即可。
还有一些其他的细节就是,
1. 给出的骨牌不是按x的从小到大给出的自己一开始要排序。
2. 每个位置上的骨牌数量有多个,应该取最高的那个。
我是用 map + 离散化 处理的~~~。
实现的时候建了两棵线段树, 码量略大~~~~
code:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
#define clr(x, y) memset(x, y, sizeof x)
#define inf 1000000000
using namespace std;
const int maxn = 100005;
typedef long long LL;
LL xi[maxn];
LL hi[maxn];
// pl向左最远编号
// pr向右最远编号
int pl[maxn], pr[maxn];
int n;
// va means dp[i][0]
// vb means dp[i][1]
int va[maxn<<2], col[maxn<<2];
int vb[maxn<<2];
int ma[maxn], mb[maxn];
map<LL, LL> mi;
//-------
void pushup_va(int rt)
{
va[rt] = min(va[rt<<1], va[rt<<1|1]);
}
void pushdown_va(int rt)
{
col[rt<<1] = col[rt<<1|1] = col[rt];
va[rt<<1] = min(va[rt<<1], col[rt<<1]);
va[rt<<1|1] = min(va[rt<<1|1], col[rt<<1|1]);
col[rt] = -1;
}
void update_va(int L, int R, int c, int l, int r, int rt)
{
if (L <= l && r <= R)
{
col[rt] = c;
va[rt] = min(va[rt], col[rt]);
return ;
}
if (col[rt] != -1) pushdown_va(rt);
int m = (l + r)>>1;
if (L <= m) update_va(L, R, c, l, m, rt<<1);
if (R > m) update_va(L, R, c, m+1, r, rt<<1|1);
pushup_va(rt);
}
int query_va(int p, int l, int r, int rt)
{
if (l == r)
{
//printf("p=%d va=%d col=%d\n",p,va[rt],col[rt]);
return va[rt];
}
if (col[rt] != -1) pushdown_va(rt); //--*
int m = (l + r)>>1;
if (p <= m) return query_va(p, l, m, rt<<1);
else return query_va(p, m+1, r, rt<<1|1);
}
//-------
void pushup_vb(int rt)
{
vb[rt] = min(vb[rt<<1], vb[rt<<1|1]);
}
void update_vb(int p, int c, int l, int r, int rt)
{
if (l == r)
{
vb[rt] = c;
return ;
}
int m = (l + r) >> 1;
if (p <= m) update_vb(p, c, l, m, rt<<1);
else update_vb(p, c, m+1, r, rt<<1|1);
pushup_vb(rt);
}
int query_vb(int L, int R, int l, int r, int rt)
{
if (L <= l && r <= R) return vb[rt];
int m = (l + r) >> 1;
int ans = inf;
if (L <= m) ans = min(ans, query_vb(L, R, l, m, rt<<1));
if (R > m) ans = min(ans, query_vb(L, R, m + 1, r, rt<<1|1));
return ans;
}
void build(int l, int r, int rt)
{
va[rt] = vb[rt] = inf;
col[rt] = -1;
if (l == r) return ;
int m = (l + r) >> 1;
build(l, m, rt<<1);
build(m+1, r, rt<<1|1);
pushup_va(rt);
pushup_vb(rt);
}
int main()
{
//freopen("input.txt", "r", stdin);
while(scanf("%d", &n) == 1)
{
mi.clear();
for (int i = 1; i <= n; i++)
{
scanf("%lld%lld", &xi[i], &hi[i]);
if(mi.count(xi[i]) == 0)
mi[xi[i]] = hi[i];
else
mi[xi[i]] = max(mi[xi[i]], hi[i]);
}
xi[0] = -1;
sort(xi, xi + n + 1);
int len = unique(xi, xi + n + 1) - xi;
n = len - 1;
for (int i = 1; i <= n; i++)
{
hi[i] = mi[xi[i]];
pl[i] = lower_bound(xi+1, xi + n + 1, xi[i] - hi[i]) - xi;
pr[i] = upper_bound(xi+1, xi + n + 1, xi[i] + hi[i]) - xi - 1;
}
// for (int i = 1; i <= n; i++) printf("i=%d xi=%d hi=%d pl=%d pr=%d\n", i, xi[i], hi[i], pl[i], pr[i]);
build(1, n, 1);
clr(ma, 0); clr(mb, 0);
for (int i = 1; i <= n; i++)
{
int da = inf;
int db = inf;
db = min(db, query_vb(pl[i], i, 1, n, 1));
int mid = min(ma[pl[i] - 1], mb[pl[i] - 1]);
db = min (db, mid + 1);
da = min(da, query_va(i, 1, n, 1));
//cout<<"i="<<i<<"da="<<da<<endl;
da = min(da, mb[i - 1] + 1);
da = min(da, ma[i - 1] + 1);
//cout<<"i="<<i<<"da="<<da<<endl;
ma[i] = da; mb[i] = db;
update_va(i, pr[i], da, 1, n, 1);
update_vb(i, db, 1, n, 1);
}
//for (int i = 1; i <= n; i++) printf("i = %d ma = %d mb = %d\n", i, ma[i], mb[i]);
printf("%d\n", min(ma[n], mb[n]));
}
return 0;
}
加油~~