标签:
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3650
题意: 给你n个骨牌,每个骨牌有一个在x轴上的位置和高度,每个骨牌可以想左推也可以向右推,问最少多少步可以把骨牌全部推倒。
思路:
之前Goodbye 2014 上的E题就是一道多米诺骨牌的题目,虽然跟这道题目不太一样但是还是提供了一下思路, 附题解的链接:
http://blog.csdn.net/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; }
标签:
原文地址:http://blog.csdn.net/u013649253/article/details/46272331