928 字
5 分钟
便
2017-08-15

题目描述#

给出一个R*C的棋盘.共有R行C列,R*C个格子.现要在每个格子都填一个非负整数.使得任意一个2*2的正方形区域都满足这样的性质:左上角的数字+右下角的数字=左下角的数字+右上角的数字.有些格子已经确定,你不能更改其中的数字.其他格子的数字由你决定. 这是一个符合要求的3*3的棋盘:

123
234
456

不难验证每个2*2的区域都是符合要求的. Orbitingflea想要知道一个可行的填充棋盘的方案.但是这个方案可能很大.所以你只需对给定的棋盘判定是否存在至少一种可行的填充棋盘的方案.

输入#

第一行输入一个T,表示数据组数。接下来T组数据。

每组数据的第1行2个整数R,C表示棋盘的大小.

第2行1个整数n表示已经被填好数字的格子的数目.

接下来n行每行3个整数ri,ci,ai,表示第ri行ci列的格子被填上了数字ai.

输出#

T行.第i行是第i组数据的答案.有合法方案时输出一行Yes,没有时输出一行No.

样例输入#

6
2 2 3
1 1 0
1 2 10
2 1 20
2 3 5
1 1 0
1 2 10
1 3 20
2 1 30
2 3 40
2 2 3
1 1 20
1 2 10
2 1 0
3 3 4
1 1 0
1 3 10
3 1 10
3 3 20
2 2 4
1 1 0
1 2 10
2 1 30
2 2 20
1 1 1
1 1 -1

样例输出#

Yes
No
No
Yes
No
No

题解#

每一列差分后得到的结果相同.每一行差分后的 结果也相同.于是我们对行列分别用带权并查集维护行之间,列之间的差分关系.如果差分关系出现矛盾(两行之间的差 值可以推导出两种可能)则无解.如果差分关系没有矛盾,则需要求出整个矩阵中能推导出的最小值判断是否小于0.

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int fa1[1000005], fa2[1000005];
long long w1[1000005], w2[1000005];
long long Min1[1000005], Min2[1000005];
struct Point
{
    int x, y, v;
} a[1000005];
bool cmpx(const Point &A, const Point &B)
{
    return A.x < B.x;
}
bool cmpy(const Point &A, const Point &B)
{
    return A.y < B.y;
}
int find1(int x)
{
    if (x == fa1[x])
        return x;
    int rt = find1(fa1[x]);
    w1[x] += w1[fa1[x]];
    return fa1[x] = rt;
}
int find2(int x)
{
    if (x == fa2[x])
        return x;
    int rt = find2(fa2[x]);
    w2[x] += w2[fa2[x]];
    return fa2[x] = rt;
}
bool link1(int a, int b, int w)
{
    if (find1(a) != find1(b))
    {
        int fa = find1(a), fb = find1(b);
        fa1[fa] = fa1[fb];
        w1[fa] = w + w1[b] - w1[a];
        return 1;
    }
    else
        return w1[a] == w + w1[b];
}
bool link2(int a, int b, int w)
{
    if (find2(a) != find2(b))
    {
        int fa = find2(a), fb = find2(b);
        fa2[fa] = fa2[fb];
        w2[fa] = w + w2[b] - w2[a];
        return 1;
    }
    else
        return w2[a] == w + w2[b];
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        bool flag = 1;
        int R, C;
        scanf("%d%d", &R, &C);
        for (int i = 1; i <= R; i++)
        {
            fa1[i] = i;
            w1[i] = 0;
        }
        for (int i = 1; i <= C; i++)
        {
            fa2[i] = i;
            w2[i] = 0;
        }
        int n;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].v);
        for (int i = 1; i <= n; i++)
            if (a[i].v < 0)
                flag = 0;
        sort(a + 1, a + n + 1, cmpx);
        for (int i = 1; i < n; i++)
            if (a[i].x == a[i + 1].x)
                if (!link2(a[i].y, a[i + 1].y, a[i + 1].v - a[i].v))
                    flag = false;
 
        sort(a + 1, a + n + 1, cmpy);
        for (int i = 1; i < n; i++)
            if (a[i].y == a[i + 1].y)
                if (!link1(a[i].x, a[i + 1].x, a[i + 1].v - a[i].v))
                    flag = false;
 
        memset(Min1, 0x3f, sizeof(Min1));
        memset(Min2, 0x3f, sizeof(Min2));
        for (int i = 1; i <= n; i++)
        {
            int rt = find1(a[i].x);
            Min1[rt] = min(Min1[rt], a[i].v + w1[a[i].x]);
        }
        for (int i = 1; i <= R; i++)
        {
            int rt = find1(i);
            Min2[rt] = min(Min2[rt], -w1[i]);
        }
        for (int i = 1; i <= R; i++)
        {
            if (fa1[i] == i && Min1[i] + Min2[i] < 0)
                flag = 0;
        }
        if(flag)
            puts("Yes");
        else
            puts("No");
    }
}
便
https://www.nekomio.com/posts/101/
作者
NekoMio
发布于
2017-08-15
许可协议
CC BY-NC-SA 4.0