UVa11134 Fabled Rooks

原题链接

pdf

题目大意

在一个\(n\times n(1<=n<=5000)\)的棋盘上放置n个车,每个车都只能在给定的一个矩形(\(xl_i,xr_i,yl_i,yr_i\))里放置,使其n个车两两不在同一行和同一列,判断并给出解决方案。

题解

一道神奇的贪心题……

这道题乍一看感觉难以下手,机房大佬一看这题竟说出了二分图匹配(当时我震惊了?)。

首先,我们发现其实x轴与y轴是互不相关的,可以独立求解。于是我们把x轴与y轴分开求解。

于是问题就变成了:在\([1,n]\)的区间中,有一些区间,在每一个区间中选一个点,使最终恰好覆盖\([1,n]\)中的这\(n\)个点。

开始时,我想的是以\(l\)作为第一关键字,\(r\)作为第二关键字进行排序。然后从左往右扫。然而这样显然是不成立的,[1,3],[1,3],[2,2]这组数据就会被卡掉。

在用贪心法解决问题时,我们可以考虑:如果要选择几种状态,一种决策的“后路”覆盖了所有其他决策的“后路”,那我们不应当选择这种决策。(可能我语文不怎么好,那就用这道题解释一下吧。)

试想一下,如果我们要求从前往后做出抉择,且有两段区间都可以选择,那我们应该选择哪一段?显然是\(r\)值小的哪一段。应为对于后面的点,\(r\)值小的可以覆盖的点\(r\)值大的也可以覆盖,而\(r\)值大的可覆盖的点\(r\)值小的可能无法覆盖。这样,我们可以认为\(r\)值大的”后路“覆盖了\(r\)值小的的所有后路,于是我们应该选\(r\)值小的。

于是我们就不难想到方法:以\(r\)(后路)作为关键字排序(\(r\)相同的可以随意排),然后对于每一个序列,从\(l\)\(r\)扫描。如果是该点没有被选择过,那就选择该点。

具体实现可以看一下代码。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 5005;

struct qujian
{
    int xl, xr, yl, yr;
    int x, y;
    int num;

    inline bool operator < (const qujian& oth) const//重载小于号,方便打乱之后排回来
    {
        return num < oth.num;
    }
} qj[maxn];

inline bool cmp_x(qujian a, qujian b)//以r为关键字进行排序
{
    return a.xr < b.xr;
}

inline bool cmp_y(qujian a, qujian b)
{
    return a.yr < b.yr;
}

bool have[maxn];

inline void solve(int n)
{
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d%d%d%d", &qj[i].xl, &qj[i].yl, &qj[i].xr, &qj[i].yr);
        qj[i].num = i;//在读入时记录标号,方便打乱之后排回来
    }

    memset(have, 0, sizeof(have));//多组数据,注意初始化
    sort(qj+1, qj+n+1, cmp_x);
    for(int i = 1; i <= n; ++i)
    {
        int x = qj[i].xl;
        while(have[x] && x <= qj[i].xr)//找到第一个没有被选过的点
            x++;
        if(x > qj[i].xr)
        {
            puts("IMPOSSIBLE ");//udebug上有空格,但不加似乎也能过
            return;
        }
        else
        {
            qj[i].x = x;
            have[x] = true;
        }
    }

    memset(have, 0, sizeof(have));
    sort(qj+1, qj+n+1, cmp_y);
    for(int i = 1; i <= n; ++i)
    {
        int y = qj[i].yl;
        while(have[y] && y <= qj[i].yr)
            y++;
        if(y > qj[i].yr)
        {
            puts("IMPOSSIBLE ");
            return;
        }
        qj[i].y = y;
        have[y] = true;
    }

    sort(qj+1, qj+n+1);//排回来
    for(int i = 1; i <= n; ++i)
        printf("%d %d\n", qj[i].x, qj[i].y);
}

int main()
{
    int n;
    while(scanf("%d", &n) == 1 && n)
        solve(n);
}

转载于:https://www.cnblogs.com/pfypfy/p/9062584.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值