BOI2009 《Candy Machine》解题报告 By LTL
ltl2011/05/28软件综合 IP:福建
前一段时间我做的一道题目,感觉挺不错的,愿与大家分享。

题目简述:
在一条数轴上第ti秒会在si处掉落一个糖果,共n个,每个机器人每秒可静止不动或移动到相邻位置,求捡到所有糖果的最少机器人数。
n≤100000。

关键字:
线段树/二分查找(类似LIS)

算法描述:
以时间为x轴,位置为y轴建系,可以发现对于一个点,能到达它的机器人肯定在两条斜率k=±1的直线在它之前所夹之角的范围内,故将坐标系旋转45°可将问题转化为:平面上给的一些点,求至少要几条只能向右上走的路径可以覆盖所有点。
对于转化后的问题,可以按照将y坐标离散化(相同的y坐标按照x从小到大离散化为不同的值),然后按照x、y为第一、第二关键字从小到大扫描,每次在线段树中查询是否有y坐标当前点的点,如果有,取出y坐标最大点(并从线段树中删除),与当前点连成一条路径;如果没有,则新建一条路径。最后将当前点插入线段树。这样贪心可以保证最优(类似与NOIP《导弹拦截》第二问的贪心解法)。
通过观察可以发现,如果可以取出一个点,那么插入新的点后线段树所维护的序列中数的相对位置并没有改变,而无法取出时则必然插入在序列的头部,所以可以用一个线性表维护后二分查找,而不用线段树。

解题心得&收获:
对于题目的分析和问题的转化,旋转坐标系是一种在信息奥赛中很常见的思路,常见的题型有曼哈顿距离(由于在给定的曼哈顿距离内的点集是菱形,旋转45°后就变成了和坐标轴平行的矩形,就可以使用一些数据结构和扫描线类的方法维护)。
问题所具有的一些特殊的性质可以用来简化解法(如本题中用线性表+二分代替线段树),而不是直接就暴力用高级数据结构解决。

My Code(C++):

/*
Title: candy
Author: ltl
*/

#include <iostream>
#include <memory.h>
#include <cstdio>
#include <cstdlib>
#include <algorithm>

#define InputFileName        "XXXXXXXX"
#define OutputFileName        "candy.out"

using namespace std;

const int MaxN = 110000;

int n, Data[MaxN*5], Ans;
struct TVertex
{
    int x, y, Orix, Oriy, Num, Wagon;
}a[MaxN], b[MaxN];

inline bool cmpx(TVertex a, TVertex b)
{
    return a.x < b.x || a.x == b.x && a.y < b.y;
}

inline bool cmpy(TVertex a, TVertex b)
{
    return a.y < b.y || a.y == b.y && a.x < b.x;
}

inline bool cmpNum(TVertex a, TVertex b)
{
    return XXXXm < XXXXm;
}

inline int Min(const int a, const int b)
{
    return a < b ? a : b;
}

inline int Max(const int a, const int b)
{
    return a > b ? a : b;
}

void Init()
{
    scanf("%d", &n);
    for (int i = 1; (a[i].Num = i) <= n; ++i)
        scanf("%d%d", &a[i].y, &a[i].x);
    sort(a+1, a+n+1, cmpx);
    int MaxYX = -0x7FFFFFFF, MinXY = 0x7FFFFFFF;
    for (int i = 1; i <= n; ++i)
    {
        MaxYX = Max(MaxYX, a[i].y-a[i].x);
        MinXY = Min(MinXY, a[i].x+a[i].y);
    }
    for (int i = 1; i <= n; ++i)
    {
        b[i].x = MaxYX-(a[i].y-a[i].x);
        b[i].y = a[i].x+a[i].y-MinXY;
        b[i].Orix = a[i].x;
        b[i].Oriy = a[i].y;
        b[i].Num = a[i].Num;
    }
    sort(b+1, b+n+1, cmpy);
    for (int i = 1; i <= n; ++i)
        b[i].y = i;
    sort(b+1, b+n+1, cmpx);
}

void Insert(const int t, const int l, const int r, const int p, const int k)
{
    if (l == r)
    {
        Data[t] = k;
        return;
    }
    const int mid = l+r >> 1;
    if (p <= mid)
        Insert(t << 1, l, mid, p, k);
    else
        Insert((t << 1)+1, mid+1, r, p, k);
    Data[t] = Max(Data[t << 1], Data[(t << 1)+1]);
}

int Find(const int t, const int l, const int r, const int p)
{
    if (p >= r)
        return Data[t];
    const int mid = l+r >> 1;
    int Res = Find(t << 1, l, mid, p);
    if (p > mid)
        Res = Max(Res, Find((t << 1)+1, mid+1, r, p));
    return Res;
}

void Print()
{
    printf("%d\n", Ans);
    for (int i = 1; i <= n; ++i)
        printf("%d %d %d\n", b[i].Oriy, b[i].Orix, b[i].Wagon);
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen(InputFileName, "r", stdin);
    freopen(OutputFileName, "w", stdout);
    #endif
    Init();
    for (int i = 1, p; i <= n; ++i)
    {
        p = Find(1, 1, n, b[i].y);
        if (p)
        {
            Insert(1, 1, n, p, 0);
            b[i].Wagon = b[p].Wagon;
        }
        else
            b[i].Wagon = ++Ans;
        Insert(1, 1, n, b[i].y, b[i].y);
    }
    sort(b+1, b+n+1, cmpNum);
    Print();
    return 0;
}


题目来源:
BOI2009
+500  科创币    joyeep    2011/05/29 思路明确
来自:计算机科学 / 软件综合
6
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
ltl 作者
13年9个月前 IP:未同步
297036
用code括起来的东西行距怎么调?
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
ltl作者
13年9个月前 IP:未同步
297140
编程版块也太冷清了吧,这让我作为一个OIer情何以堪(虽然KC主业是化学电武能才火箭)
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
joyeep
13年9个月前 IP:未同步
297153
回 2楼(ltl) 的帖子
对编程强烈兴趣的本坛子不多,远远不及其他几个板块;

或许趣味强的,更为直观的能引起大家的主要;

比如:机器人对弈,AI实际用途,图形图像,等;
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
ltl作者
13年9个月前 IP:未同步
297335
我想搞NOI系列赛的还是不少的吧
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
ltl作者
13年9个月前 IP:未同步
297837
AI在NOI中也有,叫做团体对抗赛,是以省队为单位编写AI进行游戏对战
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
上级专业
同级专业
ltl
学者 笔友
文章
50
回复
3650
学术分
2
2009/09/27注册,3个月0天前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}