CodeChef Protecting The Poison(贪心)

 题意

蛇之王国是一个 N × N 的网格,他们最宝贵的藏品就是一个巨大的毒药库,存储在王国中央

K × K 的区域中。保证 N 和 K 都是奇数。假设网格中 (1, 1) 为左上角,(N, N) 为右下角,那么

中央的 K × K 区域的左上角格子为 ((N - K)/2 + 1,(N - K)/2 + 1)。

但有贼想要盗走这些毒药。他们没法进入王国 N × N 的领地中,但他们从可以外面射箭。这

些箭可以横穿一整行(从左至右或从右至左)或者纵跨一整列(从上至下或者从下至上)。如果箭

穿过了 K × K 的部分,一些毒药就会沾到箭上;之后这支箭飞出 N × N 的领地之后,贼就能获

得一部分毒药了。

作为蛇之王国的国王,你希望可以组织这些盗贼。你知道,如果箭射到了蛇身上,蛇身上的

鳞片会挡住箭,让它停下,但蛇不会受伤。已经有一些蛇在保卫毒药了。每条蛇占据了 N × N 网

格中连续一条线上的格子,以及一行或者一列的一部分。两条蛇可以占据相同的部分格子。如果

这些蛇可以组阻止盗走毒药,那么我们称蛇的布局是安全的。阻止盗走毒药,即无论从哪行哪列

的哪一侧射箭,要么无法经过 K × K 的部分,要么无法离开 N × N 的领地。

国王还要给众蛇安排其他任务,因此想要将尽可能多的保卫蛇调走去干别的工作,但仍然使

得剩下的蛇的布局是安全的。请你帮他求出最少得留下多少条蛇。


数据

输入的第一行包含一个整数 T,代表测试数据的组数。接下来是 T 组数据。

每组数据第一行包含三个整数 N、K 和 M,分别代表领地边长、毒药库边长,以及初始时保卫蛇的条数。

接下来 M 行,每行包含四个整数 HX[i]、HY[i]、TX[i] 和 TY[i]。(HX[i], HY[i])是第i条蛇的头部

坐标,(TX[i], TY[i]) 是第i条蛇的尾部坐标。保证这两个坐标对应的格子位于同行或同列,第i条蛇占据两个格子之间的所有格子。

1 <= T <= 4, 3 <= N <= 1e9, 1 <= K <= N - 2, N和K都是奇整数

1 <= M <= 1e5, 1 <= HX[i], HY[i], TX[i], TY[i] <= N, HX[i] = TX[i]或者HY[i] = TY[i]

K × K的区域不会被任意一条蛇占据,两条蛇占据的位置可能有重合部分


输入

2

7 3 7

1 1 6 1

1 2 3 2

5 2 5 2

2 4 2 6

6 2 6 4

5 6 5 7

7 1 7 4

7 3 7

1 1 6 1

1 2 3 2

5 2 5 2

2 6 2 6

6 2 6 4

5 6 5 7

7 1 7 4


输出

3

-1


思路:蛇不会在中间那块,所以每条蛇挡的不是上下就是左右方向,所以分别考虑,这样问题就变成了最小区间覆盖问题了,可以按左端点排序,每次选已经覆盖的区间中右端点最大的区间。


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1e6+5;
struct node
{
    int l, r;
    node(){}
    node(int ll, int rr): l(ll), r(rr) {}
    bool operator < (const node &a) const
    {
        if(l == a.l) return r > a.r;
        return l < a.l;
    }
}a[maxn], b[maxn];
int n, k, m, cnt1, cnt2;
bool ok;

int cal(node *c, int cnt)
{
    int s = (n-k)/2+1, e = (n+k)/2;
    int res = 0, p = 0;
    int cur = s, Max = 0, index;
    for(int i = 0; i < cnt; i++)
    {
        for(int j = i; j < cnt; j++)
        {
            if(c[j].l > cur)
                break;
            else
            {
                if(c[j].r+1 > Max)
                    Max = c[j].r+1, index = j;
            }
        }
        if(!Max) { ok = 0; return 0; }
        else
        {
            cur = Max;
            res++;
            i = index;
            Max = 0;
            if(cur > e) return res;
        }
    }
    ok = 0;
    return 0;
}

int main(void)
{
    int t;
    cin >> t;
    while(t--)
    {
        ok = 1;
        cnt1 = cnt2 = 0;
        scanf("%d%d%d", &n, &k, &m);
        for(int i = 1; i <= m; i++)
        {
            int x1, y1, x2, y2;
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            if(x1 == x2)
            {
                if(y1 > y2) swap(y1, y2);
                if(x1 >= (n-k)/2+1 && x1 <= (n+k)/2)
                    b[cnt2++] = node(x1, x2);
                else
                    a[cnt1++] = node(y1, y2);
            }
            else
            {
                if(x1 > x2) swap(x1, x2);
                if(y1 >= (n-k)/2+1 && y1 <= (n+k)/2)
                    a[cnt1++] = node(y1, y2);
                else
                    b[cnt2++] = node(x1, x2);
            }
        }
        sort(a, a+cnt1);
        sort(b, b+cnt2);
        int ans = cal(a, cnt1)+cal(b, cnt2);
        printf("%d\n", ok ? ans : -1);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值