poj 2296 Map Labeler (2-sat +二分)

Description

Map generation is a difficult task in cartography. A vital part of such task is automatic labeling of the cities in a map; where for each city there is text label to be attached to its location, so that no two labels overlap. In this problem, we are concerned with a simple case of automatic map labeling. 

Assume that each city is a point on the plane, and its label is a text bounded in a square with edges parallel to x and y axis. The label of each city should be located such that the city point appears exactly in the middle of the top or bottom edges of the label. In a good labeling, the square labels are all of the same size, and no two labels overlap, although they may share one edge. Figure 1 depicts an example of a good labeling (the texts of the labels are not shown.) 

Given the coordinates of all city points on the map as integer values, you are to find the maximum label size (an integer value) such that a good labeling exists for the map. 

Input

The first line contains a single integer t (1 <= t <= 10), the number of test cases. Each test case starts with a line containing an integer m (3 ≤ m ≤ 100), the number of cities followed by m lines of data each containing a pair of integers; the first integer (X) is the x and the second one (Y) is the y coordinates of one city on the map (-10000 ≤X, Y≤ 10000). Note that no two cities have the same (x, y) coordinates.

Output

The output will be one line per each test case containing the maximum possible label size (an integer value) for a good labeling.

Sample Input

1
6
1 1
2 3
3 2
4 4
10 4
2 5

Sample Output

2

Source

Tehran 2003


题意:给出以下二维坐标点,然后让你往平面上放正方形,点必须落在正方形上面边的中点或者下面边的中点,正方形不能重叠,可以共用边。问最大正方形边的边长。

分析:这种最大化最小值或者最小化最大值的问题,我们都可以种二分+判断的方法来解,这个也不例外,关键是判断部分 
我们现在二分枚举边长为diff,然后所有的点就变成了在正方形上面或者下面的问题了,二选一的问题很明显可以用2-set来判断的 
这个题目的关键在于理解他们之间的二元关系并建图,下面我们来分析

首先,对于一个点a,在正方形的上边上,设为点a,在下边上设为点-a。 
b同样的,上边上设为点b,下边上设为-b 
对于任意两个点a和b 
如果a和b的横坐标只差小于diff 
如果纵坐标值相等,那么只能是一个在上面一个在下面,表示而二元关系为(a | b)即a和b只能有一个在上边上,改写成等价形式 
(a&-b)|(-a&b)即a在上边则b必然在下边,a在下边的话b必然在上边,我们在加边的时候要求强连通分量,所以这里可以按无向边建图。 
如果纵坐标的值小于diff,则纵坐标值大的所在正方形必然位于上面,小的位于下面,我们假设a的纵坐标大于b,我们可以让a–>-a,-b–>b,为什么呢?因为我们必然要让a的四边形位于上面,即点在下边上,这样连边如果点在上边,则会形成a个-a属于同一个强连通分量导致可行,so.. 
如果纵坐标的值小于2*diff,同样假设a纵坐标的大于b,那么a的四边形在下,则b的必然在下,b的在上则a的必然也在上所以-a–>-b,b–>a 
这用只要同s-set跑是否可行就ok 
对其中的一些关系的建图还是很经典的。 


代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stack>
using namespace std;
stack<int>s;
const int N=105;
struct node{
    int x,y;
}point[N];
struct node1{
    int now,next;
}str[N*N*4];
int head[2*N],tot;
int dfs[2*N],ins[2*N],belong[2*N],low[2*N],ans,tol;
void add(int x,int y)
{
    str[tot].now=y;
    str[tot].next=head[x];
    head[x]=tot++;
}
void tarjan(int u)
{
    ins[u]=1;
    dfs[u]=low[u]=tol++;
    s.push(u);
    for(int i=head[u];i!=-1;i=str[i].next)
    {
        int v=str[i].now;
        if(dfs[v]==-1)
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(ins[v])
            low[u]=min(low[u],dfs[v]);
    }
    if(dfs[u]==low[u])
    {
        while(1)
        {
            int q=s.top();
            s.pop();
            ins[q]=0;
            belong[q]=ans;
            if(q==u) break;
        }
        ans++;
    }
}
bool ok(int n)
{
    memset(dfs,-1,sizeof(dfs));
    memset(ins,0,sizeof(ins));
    ans=tol=0;
    for(int i=0;i<2*n;i++)
        if(dfs[i]==-1)
            tarjan(i);
    for(int i=0;i<2*n;i+=2)
        if(belong[i]==belong[i+1])
            return false;
    return true;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d%d",&point[i].x,&point[i].y);
        int l=0,r=80000;
        while(r>=l)
        {
            int temp=(l+r)/2;
            memset(head,-1,sizeof(head));
            tot=0;
            for(int i=0;i<n-1;i++)
            {
                for(int j=i+1;j<n;j++)
                {
                    if(abs(point[i].x-point[j].x)>=temp) continue;
                    if(abs(point[i].y-point[j].y)<temp)
                    {
                        if(point[i].y==point[j].y)
                        {
                            add(2*i,2*j+1);
                            add(2*i+1,2*j);
                            add(2*j+1,2*i);
                            add(2*j,2*i+1);
                        }
                        else if(point[i].y>point[j].y)
                        {
                            add(2*i+1,2*i);
                            add(2*j,2*j+1);
                        }
                        else
                        {
                            add(2*j+1,2*j);
                            add(2*i,2*i+1);
                        }
                    }
                    else if(abs(point[i].y-point[j].y)<2*temp)
                    {
                        if(point[i].y>point[j].y)
                        {
                            add(2*i+1,2*j+1);
                            add(2*j,2*i);
                        }
                        else
                        {
                            add(2*j+1,2*i+1);
                            add(2*i,2*j);
                        }
                    }
                }
            }
            if(ok(n)) l=temp+1;
            else r=temp-1;
        }
        printf("%d\n",r);
    }
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值