码迷,mamicode.com
首页 > 其他好文 > 详细

ZOJ 3650(多米诺骨牌 dp + 线段树优化)

时间:2015-05-30 12:15:34      阅读:140      评论:0      收藏:0      [点我收藏+]

标签:

题目链接: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;
}

加油~~

ZOJ 3650(多米诺骨牌 dp + 线段树优化)

标签:

原文地址:http://blog.csdn.net/u013649253/article/details/46272331

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!