Gym 101201F Illumination - 2SAT

1000*1000的矩形区域内给定$n(n \leq 1000)$盏灯,每盏灯可以横着亮或者竖着亮并照亮该方向上加上自己的$2 l + 1$个格点,要求点亮所有的灯,使得没有两个同一方向上点亮的灯有重合照亮的位置。

链接

F - Illumination

题解

可以发现每盏灯只有两个状态横着点亮或者竖着点亮,用$0/1$表示,那么可以$O(n^2)$枚举两盏灯在某个点亮方向上是否和另外一盏灯冲突,这个限制即两盏灯的状态不能同时为$0或1$,由这个逻辑关系,剩下的就是2-SAT的事了。

代码

/*
* Filename:    2-SAT.cpp
* Created:     Wednesday, September 13, 2017 04:06:32 PM
* Author:      crazyX
* More:
*
*/
#include <bits/stdc++.h>

#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define SZ(x) ((int) (x).size())
#define all(x) (x).begin(), (x).end()
#define sqr(x) ((x) * (x))
#define clr(a,b) (memset(a,b,sizeof(a)))
#define y0 y3487465
#define y1 y8687969
#define fastio std::ios::sync_with_stdio(false)

using namespace std;

typedef long long ll;

const int INF = 1e9 + 7;
const int maxn = 1e3 + 7;

struct Twosat {
    int n;
    vector<int> g[maxn * 2];
    bool mark[maxn * 2];
    int S[maxn * 2], c;

    bool dfs(int x) {
        if (mark[x ^ 1]) return 0;
        if (mark[x]) return 1;
        mark[x] = 1;
        S[c++] = x;
        for (int i = 0; i < SZ(g[x]); i += 1)
            if (!dfs(g[x][i])) return 0;
        return 1;
    }

    void init(int n) {
        this->n = n;
        for (int i = 0; i < n * 2; i += 1) g[i].clear();
        clr(mark, 0);
    }

    // x=xval || y=yval
    void add_clause(int x, int xval, int y, int yval) {
        x = x * 2 + xval;
        y = y * 2 + yval;
        g[x ^ 1].pb(y);
        g[y ^ 1].pb(x);
    }

    bool solve() {
        for (int i = 0; i < n * 2; i += 2) {
            if (!mark[i] && !mark[i + 1]) {
                c = 0;
                if (!dfs(i)) {
                    while (c > 0) mark[S[--c]] = 0;
                    if (!dfs(i + 1)) return 0;
                }
            }
        }
        return 1;
    }
}tsat;

int n, m, R;
int r[maxn], c[maxn];

int main()
{
#ifdef AC
    freopen("data.in", "r", stdin);
    //freopen("data.out", "w", stdout);
#endif
    scanf("%d%d%d", &n, &R, &m);
    tsat.init(m);
    for (int i = 0; i < m; i += 1) scanf("%d%d", &r[i], &c[i]);
    for (int i = 0; i < m; i += 1)
        for (int j = i + 1; j < m; j += 1) {
            if (r[i] == r[j] && abs(c[i] - c[j]) <= R)
                tsat.add_clause(i + 1, 0, j + 1, 0);
            if (c[i] == c[j] && abs(r[i] - r[j]) <= R)
                tsat.add_clause(i + 1, 1, j + 1, 1);
        }
    if (tsat.solve()) puts("YES");
    else puts("NO");
    return 0;
}

文章作者: crazyX
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 crazyX !
评论
 上一篇
Gym 101201G Maximum Islands 最大点权独立集 Gym 101201G Maximum Islands 最大点权独立集
给定一个$n × m$的矩阵,每个点可能是陆地、水面或者未定,求最多的陆地联通块的数量。 链接G - Maximum Islands 题解首先对于已经给定的图,有一个明显的贪心是,对于每个陆地,周围的未定点我们都可以看做水面,因为如果将
2017-09-14
下一篇 
Ubuntu 16.04 Apt Update Stuck %0 Ubuntu 16.04 Apt Update Stuck %0
今天使用Ubuntu 16.04时,执行apt update时卡在了某个地方: 0% [Connecting to security.ubuntu.com (2001:67c:1360:8001::17)] 就是连接不上更新服务器,一个很
2017-09-10
  目录