POJ2296题解 2-SAT+二分

题目链接
每个城市都要在lable边的中点处,且lable要么朝下,要么朝上,很明显的2-SAT问题特征。因为每个lable的边长都要相同,且范围给定,因此可以用二分进行枚举。

分点
i2表示i城市的lable朝下,i2+1表示i城市的lable朝上,一共2n个点

建边
根据枚举的lable长度length,有三种情况:
前提是两个城市的x相差小于length,不然怎么都不会影响
1.两个城市的y相同,那么必须是一上一下
2.两个城市的y相差小于length,必须是下面的城市朝下,上面的城市朝上
3.两个城市的y相差小于2length,那么当下面的城市朝上时上面的城市必须朝上,上面的城市朝下时下面的城市也必须朝下。
在代码中实现就是:

//由于按y的大小预先进行过排序,始终有p[j].y>=p[i].y
if (p[j].y == p[i].y) //y重合,一上一下
{
    addEdge(i, 1, j, 0);
    addEdge(i, 0, j, 1);
    addEdge(j, 1, i, 0);
    addEdge(j, 0, i, 1);
}
else if (p[j].y - p[i].y < length) //只能j上i下
{
    addEdge(j, 0, j, 1);
    addEdge(i, 1, i, 0);
}
else if (p[j].y - p[i].y < 2 * length) //i上j上,j下i下
{
    addEdge(i, 1, j, 1);
    addEdge(j, 0, i, 0);
}

AC代码

//AC 16MS
//2021/10/08
#include <algorithm>
#include <iostream>
#include <string.h>
#include <vector>
#define maxn 205
using namespace std;

struct point
{
    int x, y;
} p[105];
inline bool cmp(point &a, point &b) { return a.y < b.y; }
int t, n, sta[maxn], cnt;
vector<int> g[maxn];
bool vis[maxn];

void init()
{
    cnt = 0;
    memset(vis, false, sizeof(vis));
    for (int i = 0; i < 2 * n; i++)
        g[i].clear();
}
inline void addEdge(int pa, int dira, int pb, int dirb)
{
    //+1表示在上
    g[pa * 2 + dira].push_back(pb * 2 + dirb);
}
bool dfs(int x)
{
    if (vis[x ^ 1]) return false;
    if (vis[x]) return true;
    vis[x] = true;
    sta[cnt++] = x;
    for (int i = 0; i < g[x].size(); i++)
        if (!dfs(g[x][i])) return false;
    return true;
}
bool check(int length)
{
    init();
    //加边
    for (int i = 0; i < n; i++)
        for (int j = i + 1; j < n; j++)
            if (abs(p[i].x - p[j].x) < length)
            {
//由于排过序,始终有p[j].y>=p[i].y
if (p[j].y == p[i].y) //y重合,一上一下
{
    addEdge(i, 1, j, 0);
    addEdge(i, 0, j, 1);
    addEdge(j, 1, i, 0);
    addEdge(j, 0, i, 1);
}
else if (p[j].y - p[i].y < length) //只能j上i下
{
    addEdge(j, 0, j, 1);
    addEdge(i, 1, i, 0);
}
else if (p[j].y - p[i].y < 2 * length) //i上j上,j下i下
{
    addEdge(i, 1, j, 1);
    addEdge(j, 0, i, 0);
}
            }
    for (int i = 0; i < n * 2; i += 2)
    {
        if (!vis[i] && !vis[i + 1])
        {
            cnt = 0;
            if (!dfs(i))
            {
                while (cnt > 0)
                    vis[sta[--cnt]] = false;
                if (!dfs(i + 1)) return false;
            }
        }
    }
    return true;
}

int main()
{
    cin >> t;
    while (t--)
    {
        cin >> n;
        for (int i = 0; i < n; i++)
            scanf("%d%d", &p[i].x, &p[i].y);
        sort(p, p + n, cmp);
        int l = 0, r = 20001, mid, ans = 0;
        while (l < r)
        {
            mid = (l + r) / 2;
            if (check(mid))
            {
                ans = mid;
                l = mid + 1;
            }
            else
                r = mid;
        }
        printf("%d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值