[Offer收割]编程练习赛2 hihocoder 1273 (DFS + 状压)

66 篇文章 0 订阅
时间限制:5000ms
单点时限:1000ms
内存限制:256MB

描述

小Hi实验室所在的建筑一楼有一个用于贴海报的黑板,不停的有新的海报往上贴,也会安排人员不断的对海报进行清理,而最近,轮到了小Hi去对海报进行清理。

黑板是一块W*H大小的区域,如果以左下角为直角坐标系的话,在上次清理后第i张贴上去的海报可以视作左下角为(X1i, Y1i),右上角为(X2i, Y2i)的一个矩形。

撕去一张海报会导致所有覆盖在其上的海报都被同时撕掉(这样被称为连带,这个过程是具有传递性的,即如果A覆盖B,B覆盖C,那么撕掉C会导致A和B均被撕掉),但是一张海报想要被手动撕掉的话需要至少存在一个角没有被其他海报覆盖(海报A被海报B覆盖当且仅当他们存在面积大于0的交集并且A在B之前贴出,海报A的一个角被海报B覆盖当且仅当这个顶点处于海报B的内部)。

于是现在问题来了,为了节约时间,小Hi决定一次性撕掉尽可能多的海报,那么他应该选择哪张海报呢?在效果相同的情况下,小Hi倾向于选择更早贴出的海报。

输入

每个输入文件仅包含单组测试数据。

每组测试数据的第一行为三个正整数W,H和N,分别表示黑板的宽、高以及目前张贴出的海报数量。

接下来的N行,每行为四个正整数X1i、Y1i、X2i和Y2i,描述第i张贴出的海报。

对于20%的数据,满足1<=N<=5,1<=W,H<=10

对于100%的数据,满足1<=N<=1000,0<=X1i, X2i <= W, 0<=Y1i, Y2i<=H, 1<=W,H<=108

输出

对于每组测试数据,输出两个正整数Ans和K,表示小Hi一次最多能撕掉多少张海报,和他选择的海报是第几张贴出的。

样例输入
6 7 4
0 0 4 4
1 0 3 4
1 4 4 6
0 0 3 5
样例输出
3 1

题目链接:http://hihocoder.com/problemset/problem/1273

题目分析:枚举面与面之间的覆盖关系,用vector存,然后计算撕从开始到结束的每张海报一次能撕掉多少,这里要注意当前海报的四个角都被覆盖的情况,用状态压缩来记录即可

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int const MAX = 1e3 + 5;
int w, h, n;
int x1[MAX], y1[MAX], x2[MAX], y2[MAX];
int cnt[MAX];
vector<int> vt[MAX];

int Cal(int i, int j) 
{
    int res = 0;
    if(x1[i] > x1[j] && x1[i] < x2[j] && y1[i] > y1[j] && y1[i] < y2[j])
        res |= 1;
    if(x1[i] > x1[j] && x1[i] < x2[j] && y2[i] > y1[j] && y2[i] < y2[j])
        res |= 2;
    if(x2[i] > x1[j] && x2[i] < x2[j] && y2[i] > y1[j] && y2[i] < y2[j])
        res |= 4;
    if(x2[i] > x1[j] && x2[i] < x2[j] && y1[i] > y1[j] && y1[i] < y2[j])
        res |= 8;
    return res;
}

void DFS(int st, int u, int &sta) 
{
    cnt[u] = 1;
    int sz = vt[u].size();
    for(int i = 0; i < sz; i++) 
    {
        int v = vt[u][i];
        if(!cnt[v])
        {
            sta |= Cal(st, v);
            DFS(st, v, sta);
        }
    }
}

int main() 
{
    int min_x, max_x, min_y, max_y;
    scanf("%d %d %d", &w, &h, &n);
    for(int i = 1; i <= n; i++) 
        scanf("%d %d %d %d", &x1[i], &y1[i], &x2[i], &y2[i]);
    for(int i = 1; i <= n; i++)
    {
        for(int j = i + 1; j <= n; j++)
        {
            min_x = max(x1[i], x1[j]);
            max_x = min(x2[i], x2[j]);
            min_y = max(y1[i], y1[j]);
            max_y = min(y2[i], y2[j]);
            if(min_x < max_x && min_y < max_y)
                vt[i].push_back(j);
        }
    }
    int ans = 0, id = 0;
    for(int i = 1; i <= n; i++) 
    {
        int sta = 0;
        memset(cnt, 0, sizeof(cnt));
        DFS(i, i, sta);
        if(sta == 15) 
            continue;
        int cur = 0;
        for(int j = 1; j <= n; j++) 
            cur += cnt[j];
        if(cur > ans) 
        {
            ans = cur;
            id = i;
        }
    }
    printf("%d %d\n", ans, id);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值