现在的位置: 首页 > 综合 > 正文

poj 1769

2012年08月31日 ⁄ 综合 ⁄ 共 1603字 ⁄ 字号 评论关闭

POJ 1769 Minimizing maximizer

线段树之点树将最大的数字分离到最后的一位!

题意分析如果我们考虑将数组看成一条[1, n]的线段而每项操作也看成是从[ i[k], j[k] ]的线段那么题意就是按照线段的输入顺序将线段[1, n]从左到右依次覆盖问题变成求最小的覆盖线段总数.

算法思想考虑最基本的规划方法Opt[k] 表示覆盖掉[1, k]的线段最少需要的步数那么状态转移方程为:

Opt[k] = min { Opt[d] + 1 | j[p] = k && d >= i[p] && d <= j[p] && k > 1 }

其中i[p], j[p]分别表示线段p的左端点和右端点预处理: Opt[1] = 0 .

最后的答案就是Opt[n], 但是考虑时间复杂度是O(m^2), m 最大为500000, 超时无疑.但是这里我们看到了规划的决策集合是一条连续的线段是要在这条线段上面取得最小值那么线段树的结构就正好适用.

由于这里最小的单位是一个点所以我们采取线段树的第一种变化把元线段设置为单位点[k, k], 在规划的时候维护线段树即可.

另外线段树结点结构中需要加入元素 xmin, 代表最少需要用到的覆盖线段数目可以覆盖到当前结点所代表的线段.

p.s. : 如果题目不要求按照顺序选择线段从左到有覆盖的话,可以对线段进行排序,然后贪心即可.


#include <stdio.h>
const int N = 50001;
const int MAX = 500001;
struct TreeNode
{
    int b, e; // [b, e]
    int xmin;
}node[N*3];
// 建立线段树(点树)
void Bulid(int p, int l, int r)
{
    node[p].b = l; node[p].e = r; node[p].xmin = MAX;
    if( l < r )
    {
       Bulid(p*2, l, (l+r) >> 1);
       Bulid(p*2+1, ((l+r) >> 1) + 1, r);
    }
}
// 维护xmin
void Insert(int p, int r, int val)
{
    if( node[p].xmin > val ) node[p].xmin = val;
    if( node[p].b != node[p].e )
    {
       int m = (node[p].b+node[p].e) >> 1;
       if( r <= m ) Insert(p*2, r, val);
       else Insert(p*2+1, r, val);
    }
}
// 取[l, r]的最小值, 以求[l, r+1]的最小值, 只需记录右端点即可
int GetMin(int p, int l, int r)
{
    // 如果[l, r]完全覆盖node[p], 直接返回node[p]的最小值
    if( node[p].b >= l && node[p].e <= r ) return node[p].xmin;
    int m = (node[p].b+node[p].e) >> 1;
    int t1 = MAX, t2 = MAX;
    if( r <= m ) t1 = GetMin(p*2, l, r);
    else if( l > m ) t2 = GetMin(p*2+1, l, r);
    else
    {
       t1 = GetMin(p*2, l, m);
       t2 = GetMin(p*2+1, m+1, r);
    }
    return (t1 > t2 ? t2 : t1);
}
int main(void)
{
    int n, m;
    while( scanf("%d%d", &n, &m) != EOF )
    {
       Bulid(1, 1, n);   // 建树
       Insert(1, 1, 0); // opt[1] = 0;
       while( m-- )
       {
           int left, right;
           scanf("%d%d", &left, &right);
           if( left < right )
           {
              int x = GetMin(1, left, right-1);
              Insert(1, right, x+1); // 只更新右端点
           }
           //printf("OK!~~\n");
       }
       printf("%d\n", GetMin(1, n, n));
    }
    return 0;
}

  


抱歉!评论已关闭.