JZOJ ???? 双色

没有传送门
题目大意

有一个 n × n 01 矩阵,矩阵的边界值已知(即上下左右四条边界 4 n 4 个格子的值已知),你要在其余格子里填上 0 1 。每个没有值的格子上一个大写字母,要求相邻的字母相同的格子的值也相同。注意:如果两个格子字母相同但是不相邻,那么没有限制;如果两个格子相邻但是字母不同,也没有限制。

要求最后填写后的矩阵不能包含:

0 0 0 0

以及
0 1 1 0

以及
1 0 0 1

以及
1 1 1 1

n 30

Limited Constraint: n 7

Special Instance:相邻格子的字母一定不同。(即随便填 01

思路

考虑 Limited Constraint,由于只有 25 个格子要填,因此直接搜索。一旦出现了不能出现的子矩阵就退出,这样可以很快出解。(但显然不可能加神级剪枝)

直接考虑 Special Instance 似乎没有突破点。我们考虑一下类似模型。还记得 APIO 某道题要求每个 2 × 2 的子矩形有奇数个 0 吗?那道题我们将个数转成了异或,这道题也能这么做吗?

由于还存在其它子矩形有奇数个 0 ,因此不能如法炮制。但是这给了我们一个思路:找到子矩形的特征,然后想办法把特征拿出来。

题目中要求相邻且字母相同的格子填的数也必须相同,我们可以考虑从相邻格是否相同出发。观察发现,在不能出现的四种子矩形中,相邻格1相同的数目要么是 0 ,要么是 4 。而对于剩下 12 种子矩形,我们可以发现相邻格相同的数目是 2

那么我们就从这里入手啦!现在我们想到要给格子染色,使得对于每个 2 × 2 的子矩形,都恰有 2 对相邻格相同。

怎么做呢?考虑网络流。我们从源点运送“相邻格相同的数目”,对于每个 2 × 2 的子矩形,我们为它们建一个结点,然后从源点向它们连一条容量为 2 的边,从它们向汇点连一条容量为 2 的边,那么有解当且仅当最大流满流。注意到,对于相邻的两个子矩形,如果相交的部分相同,那么这个“相邻格相同的数目”对这两个子矩形都有贡献。这又是一个“一对二”的模型(见我前面几篇题解),我们需要把所有 2 × 2 的子矩形分成两类,要求相邻的子矩形一定是不同的类型。显然可以按子矩形的横坐标加纵坐标的奇偶性分类。对于奇数类型的子矩形,我们从源点向它们连一条容量为 2 的边,对于偶数类型的子矩形,我们从它们向汇点连一条容量为 2 的边。对于相邻的子矩形,我们从奇数类型的子矩形向偶数类型的子矩形连一条容量为 1 的边,表示有一个共享的“相邻格相同的数目”(这里是“一对二”的体现)。那么有解当且仅当最大流满流。

如何满足某些相邻格必须相同的限制呢?显然对于这些相邻格,我们为它们对应的边设一个下限,跑有下界的最大流,有解当且仅当存在最大可行流满流。

如何满足边界已经确定的限制呢?发现我们如果要根据网络流构造方案,关键要看子矩形之间的边是否有流量,有流量就代表这相邻的两个格子填的数相同。发现除了四个角,边界上的数只会占用一个子矩形两个格子,如果相同,我们让流量减去相同的个数即可。

参考代码

不知道为什么有上下界的网络流过不了???所以写了个代替品。

不知道是不是上下界网络流写错了……QAQ

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef LL INT_PUT;
INT_PUT readIn()
{
    INT_PUT a = 0;
    bool positive = true;
    char ch = getchar();
    while (!(std::isdigit(ch) || ch == '-')) ch = getchar();
    if (ch == '-')
    {
        positive = false;
        ch = getchar();
    }
    while (std::isdigit(ch))
    {
        (a *= 10) -= ch - '0';
        ch = getchar();
    }
    return positive ? -a : a;
}
void printOut(INT_PUT x)
{
    char buffer[20];
    int length = 0;
    if (x < 0) putchar('-');
    else x = -x;
    do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
    do putchar(buffer[--length]); while (length);
}

const int maxn = 35;
int n;
char rect[maxn][maxn];

#define RunInstance(x) delete new x
struct brute
{
    int fill[maxn][maxn];
    bool check(int x, int y)
    {
        if (fill[x - 1][y - 1] == 0 &&
            fill[x - 1][y] == 0 &&
            fill[x][y - 1] == 0 &&
            fill[x][y] == 0)
            return false;
        if (fill[x - 1][y - 1] == 0 &&
            fill[x - 1][y] == 1 &&
            fill[x][y - 1] == 1 &&
            fill[x][y] == 0)
            return false;
        if (fill[x - 1][y - 1] == 1 &&
            fill[x - 1][y] == 0 &&
            fill[x][y - 1] == 0 &&
            fill[x][y] == 1)
            return false;
        if (fill[x - 1][y - 1] == 1 &&
            fill[x - 1][y] == 1 &&
            fill[x][y - 1] == 1 &&
            fill[x][y] == 1)
            return false;
        return true;
    }
    bool bFound;
    void search(int x, int y)
    {
        if (x == n)
        {
            if (check(n, n))
                bFound = true;
            return;
        }
        int newx = x;
        int newy = y + 1;
        if (newy == n)
        {
            newx++;
            newy = 2;
        }

        if (rect[x][y] == rect[x - 1][y] &&
            rect[x][y] == rect[x][y - 1])
        {
            if (fill[x - 1][y] != fill[x][y - 1])
                return;
            fill[x][y] = fill[x - 1][y];
            if (check(x, y) && (y != n - 1 || check(x, n)) && (x != n - 1 || check(n, y)))
                search(newx, newy);
        }
        else if (rect[x][y] == rect[x - 1][y])
        {
            fill[x][y] = fill[x - 1][y];
            if (check(x, y) && (y != n - 1 || check(x, n)) && (x != n - 1 || check(n, y)))
                search(newx, newy);
        }
        else if (rect[x][y] == rect[x][y - 1])
        {
            fill[x][y] = fill[x][y - 1];
            if (check(x, y) && (y != n - 1 || check(x, n)) && (x != n - 1 || check(n, y)))
                search(newx, newy);
        }
        else
        {
            fill[x][y] = 0;
            if (check(x, y) && (y != n - 1 || check(x, n)) && (x != n - 1 || check(n, y)))
                search(newx, newy);
            if (bFound) return;
            fill[x][y] = 1;
            if (check(x, y) && (y != n - 1 || check(x, n)) && (x != n - 1 || check(n, y)))
                search(newx, newy);
        }
    }

    brute() : fill(), bFound()
    {
        for (int i = 1; i <= n; i++)
        {
            fill[1][i] = rect[1][i] - '0';
            fill[i][1] = rect[i][1] - '0';
            fill[n][i] = rect[n][i] - '0';
            fill[i][n] = rect[i][n] - '0';
        }
        search(2, 2);
        puts(bFound ? "YES" : "NO");
    }
};
struct work
{
    struct NetworkFlow
    {
        struct Edge
        {
            int from, to, cap, flow;
            Edge() {}
            Edge(int from, int to, int cap) : from(from), to(to), cap(cap), flow() {}
        };
        std::vector<Edge> edges;
        std::vector<std::vector<int> > G;
        void addEdge(int from, int to, int cap)
        {
            edges.push_back(Edge(from, to, cap));
            edges.push_back(Edge(to, from, 0));
            int i = edges.size();
            G[from].push_back(i - 2);
            G[to].push_back(i - 1);
        }

        int s, t;

        std::vector<int> level;
        std::vector<int> cur;
        int Dinic(int node, int opt)
        {
            if (node == t || !opt) return opt;
            int flow = 0;
            for (int& i = cur[node]; i < G[node].size(); i++)
            {
                Edge& e = edges[G[node][i]];
                int t;
                if (level[node] + 1 == level[e.to] && e.flow < e.cap &&
                    (t = Dinic(e.to, std::min(opt, e.cap - e.flow))))
                {
                    e.flow += t;
                    edges[G[node][i] ^ 1].flow -= t;
                    flow += t;
                    opt -= t;
                    if (!opt) break;
                }
            }
            return flow;
        }
        struct Queue : private std::vector<int>
        {
            int head;
            bool empty() { return head == size(); }
            void clear() { head = 0; std::vector<int>::clear(); }
            void push(int x) { push_back(x); }
            void pop() { head++; }
            int front() { return operator[](head); }
        } q;
        std::vector<bool> vis;
        bool BFS()
        {
            q.clear();
            level.resize(G.size());
            vis.clear();
            vis.resize(G.size());
            level[s] = 0;
            vis[s] = true;
            q.push(s);
            while (!q.empty())
            {
                int from = q.front();
                q.pop();
                for (int i = 0; i < G[from].size(); i++)
                {
                    const Edge& e = edges[G[from][i]];
                    if (e.flow < e.cap && !vis[e.to])
                    {
                        vis[e.to] = true;
                        level[e.to] = level[e.from] + 1;
                        q.push(e.to);
                    }
                }
            }
            return vis[t];
        }
        int maxFlow()
        {
            int flow = 0;
            while (BFS())
            {
                cur.clear();
                cur.resize(G.size());
                flow += Dinic(s, INT_MAX);
            }
            return flow;
        }
    } nf;

    int N;
    int idx[maxn][maxn];
    int color[maxn][maxn];
    int cnt[maxn * maxn];

    int countHelper(int x, int y)
    {
        int f = 0;
        if (std::isdigit(rect[x - 1][y - 1]))
            if (rect[x - 1][y - 1] == rect[x - 1][y])
                f++;
        if (std::isdigit(rect[x - 1][y]))
            if (rect[x - 1][y] == rect[x][y])
                f++;
        if (std::isdigit(rect[x][y]))
            if (rect[x][y] == rect[x][y - 1])
                f++;
        if (std::isdigit(rect[x][y - 1]))
            if (rect[x][y - 1] == rect[x - 1][y - 1])
                f++;
        return f;
    }

    int cap[maxn][maxn];

    work() : N(), idx(), cnt(), cap()
    {
        for (int i = 2; i <= n; i++)
            for (int j = 2; j <= n; j++)
                idx[i][j] = ++N;
        color[1][2] = 1;
        for (int i = 2; i <= n; i++)
        {
            color[i][2] = !color[i - 1][2];
            for (int j = 3; j <= n; j++)
                color[i][j] = !color[i][j - 1];
        }
        nf.G.resize(N + 2);
        nf.s = 0;
        nf.t = N + 1;
        for (int i = 2; i <= n; i++)
            for (int j = 2; j <= n; j++)
            {
                int c = countHelper(i, j);
                cap[i][j] += 2 - c;
            }
        for (int i = 2; i <= n; i++)
        {
            for (int j = 2; j <= n; j++) if (!color[i][j])
            {
                if (i > 2)
                {
                    if (rect[i - 1][j - 1] == rect[i - 1][j])
                    {
                        cap[i][j]--;
                        cap[i - 1][j]--;
                    }
                    else
                        nf.addEdge(idx[i][j], idx[i - 1][j], 1);
                }
                if (i < n)
                {
                    if (rect[i][j - 1] == rect[i][j])
                    {
                        cap[i][j]--;
                        cap[i + 1][j]--;
                    }
                    else
                        nf.addEdge(idx[i][j], idx[i + 1][j], 1);
                }
                if (j > 2)
                {
                    if (rect[i - 1][j - 1] == rect[i][j - 1])
                    {
                        cap[i][j]--;
                        cap[i][j - 1]--;
                    }
                    else
                        nf.addEdge(idx[i][j], idx[i][j - 1], 1);
                }
                if (j < n)
                {
                    if (rect[i - 1][j] == rect[i][j])
                    {
                        cap[i][j]--;
                        cap[i][j + 1]--;
                    }
                    else
                        nf.addEdge(idx[i][j], idx[i][j + 1], 1);
                }
            }
        }

        int full1 = 0, full2 = 0;
        for (int i = 2; i <= n; i++)
            for (int j = 2; j <= n; j++)
                if (color[i][j])
                    full1 += cap[i][j];
                else
                    full2 += cap[i][j];
        if (full1 != full2)
        {
            puts("NO");
            return;
        }

        for (int i = 2; i <= n; i++)
            for (int j = 2; j <= n; j++)
            {
                if (cap[i][j] < 0)
                {
                    puts("NO");
                    return;
                }
                if (cap[i][j])
                {
                    if (color[i][j])
                        nf.addEdge(idx[i][j], nf.t, cap[i][j]);
                    else
                        nf.addEdge(nf.s, idx[i][j], cap[i][j]);
                }
            }

        if (nf.maxFlow() != full1)
            puts("NO");
        else
            puts("YES");
    }
};

void run()
{
    n = readIn();
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            char ch = getchar();
            while (!(std::isdigit(ch) || std::isalpha(ch))) ch = getchar();
            rect[i][j] = ch;
        }
    }

    RunInstance(work);
}

int main()
{
#ifndef LOCAL
    freopen("color.in", "r", stdin);
    freopen("color.out", "w", stdout);
#endif
    run();
    return 0;
}
总结

虽然说这道题有点难,但是这道题用的几个模型(比如 2 × 2 子矩形,网络流“一对二”模型)都是遇到过的。如果做过题并且看了题解就觉得不是特别难了,果然还是做题量才是王道?

Remark

复习一下有下界的网络流。


  1. 相邻格指:左上与右上,右上与右下,右下与左下,左下与左上,共四对。

猜你喜欢

转载自blog.csdn.net/lycheng1215/article/details/80977788