Codeforces 555B Case of Fugitive 搭桥

题意:给出n段路和m座桥。每段路的两个端点一维坐标表示。要从m座桥中选出一些桥搭在每两条相邻的路上,使得这些路全部连通。每两条相邻的路之间只能搭一座桥,且桥的两端必须恰好分别落在两段路上。如果存在一种搭桥方法,则输出Yes并按题目给定的路形成的空隙的顺序输出每座桥的序号,若不能则输出No。

题意换个说法实际上就是给定n - 1个区间和m个数,问是否能从m个数中选出n - 1个数,恰好分别落在n - 1个区间内。






觉得可以用贪心做,开始想了下因为区间有两个端点,而且位置任意,有点不好处理。后来就想先以一个端点排序,以另一个端点作为贪心准则。首先对于所有的区间,我们按右端点递增排序。左端点不予考虑。并将所有的数(即桥长)递增排序。然后依次遍历每个区间,然后从剩下的数中找最小的可以满足这个区间的数去满足这个区间,如果找不到则直接输出No。下面我们证明这个思路的正确性。用数学归纳法即可证明。

对于第一个区间[L1,R1],若有某两个数都能满足这个区间,设较小的数为min,较大的数为max,如果我们用max去满足这个区间的话:

1、若最后的解中min没有被用来满足任何区间,那么我们将max换为min,不影响结果,因此我们可以使用min去满足这个区间。

2、若最后的解中min满足了某个其他的区间 [L2,R2],则 [L2,R2] 必定排序在 [L1,R1] 的后面(因为 [L1,R1] 是第一个区间),故有R2 ≥ R1,L2 ≤ min ≤ R2。因为min也同时满足 [L1,R1],所以必有L2 ≤ R1。由max在 [L1,R1] 区间可知max ≤ R1。故有L2 ≤ min ≤ max ≤ R1 ≤ R2,即L2 ≤ max ≤ R2,也就是max同时也满足 [L2,R2] 区间。因此这个两个数分别同时满足这两个区间。我们将它们互换,并不影响结果,同样可以使得min去满足 [L1,R1]这个区间。


假如我们按上述方法满足了前k个区间。那么对于第k + 1个区间[L1,R1],若还是有两个数(这两个数暂时均未被用来满足任何区间)能满足此区间,仍设较小的数为min,较大的数为max,如果我们用max去满足这个区间的话:

1、若最后的解中min没有被用来满足任何区间,那么我们将max换为min,不影响结果,因此我们可以使用min去满足这个区间。

2、若最后的解中min满足了某个其他的区间 [L2,R2],若 [L2,R2] 排序在 [L1,R1] 之前,那么我们会在先前选择满足 [L2,R2] 这个区间的数时,就将这个数用来满足 [L2,R2] 区间了,于是在选择满足当前区间 [L1,R1] 的数时,min已经被满足于先前的区间(即 [L2,R2])。矛盾。因此 [L2,R2] 必定排序在 [L1,R1] 的后面。这样的话,按照刚才的分析,仍可以用min去满足 [L1,R1]这个区间。

故这样的贪心准则是可行的。对于每个区间要从剩下的数中找最小的满足区间的数可以二分,但是二分之后要删去这个数(因为后面不再用到)所以这里用mult_set实现。









/*----------------------

虽然是第一次打div1。。。。。

然而并不想打div1。。。。。

但是rating涨上去了又不得不打div1。。。。。

然而打了又不得不掉rating下次继续打div2。。。。。

好纠结啊。。。。。

以后只要再被逼无奈打div1。。。。。

我一定像这次一样坐等掉rating然后去打div2。。。。。

I LOVE AC

AC_FirstLucker

----------------------*/



#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
using namespace std;

struct Bridge //桥
{
    int id;
    long long lenth;
};

struct Range //区间
{
    int id;
    long long l;
    long long r;
};

const int MAX = 200005;

int n, m;
Range range[MAX];
Bridge temp[MAX]; //先用数组排序后再插入到集合中
multiset <Bridge> bridge;

bool operator < (Range r1, Range r2)
{
    return r1.r < r2.r;
}

bool operator < (Bridge b1, Bridge b2)
{
    return b1.lenth < b2.lenth;
}

void input()
{
    long long x, y, tx, ty;
    scanf("%I64d%I64d", &tx, &ty);
    for(int i = 0; i < n - 1; i++)
    {
        scanf("%I64d%I64d", &x, &y);
        range[i].id = i;
        range[i].l = x - ty;
        range[i].r = y - tx;
        tx = x;
        ty = y;
    }
    for(int i = 0; i < m; i++)
    {
        scanf("%I64d", &temp[i].lenth);
        temp[i].id = i;
    }
}

void solve()
{
    int ans[MAX]; //记录答案
    sort(range, range + n - 1);
    sort(temp, temp + m);
    bridge.clear();
    for(int i = 0; i < m; i++)
        bridge.insert(temp[i]);
    for(int i = 0; i < n - 1; i++)
    {
        Bridge value;
        value.lenth = range[i].l;
        set <Bridge>::iterator it = bridge.lower_bound(value);
        if((*it).lenth < range[i].l || (*it).lenth > range[i].r) //找不到任何可以满足当前区间的桥
        {
            printf("No\n");
            return;
        }
        ans[range[i].id] = (*it).id; //用找到的桥满足这个区间
        bridge.erase(it); //删除当前找到的桥
    }
    printf("Yes\n");
    for(int i = 0; i < n - 1; i++)
    {
        if(i > 0)
            printf(" ");
        printf("%d", ans[i] + 1);
    }
    printf("\n");
}

int main()
{
    while(scanf("%d%d", &n, &m) != EOF)
    {
        input();
        solve();
    }
    return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值