UVa10639 Square Puzzle

题目链接

        本题是2004年 ACM ICPC World Finals Warmup - 2 的 H 题,热身赛只有楼教主一人通过。

题意

        给出n个简单多边形和整数m (1≤m,n≤6),多边形的每个顶点都是[0,m]内的整数,多边形的边只可能是水平、竖直或者对角线方向,每个多边形只能旋转但不能翻转。请判断这些多边形是否恰好能拼接成边长为m的正方形(每个多边形都要使用且拼接时不能重叠)。

分析

        简单多边是没有边相互交叉且无洞的,参见wikipedia上的解释:In geometry, a simple polygon is a polygon that does not intersect itself and has no holes。

        根据题目的数据规模,可以将正方形当成m×m的网格,则最终每个网格要么被一个多边形的某部分完全填充,要么被两个多边形各自填充一个三角形,回溯法可解。

        将多边形内部也网格化,则多边形的每个网格形状是正方形或三角形。实际判断多边形填充网格的形状时需要计算点是否在多边形内,并判断填充网格后具体是三角形还是正方形。

        考虑网格网格的几个等分线交点c1、c2、c3、c4(见上图),计算它们在多边形内外的实际结果即可判定填充结果是正方形还是多边形:4个点都在多边形内则为正方形、C1与C2(或C3与C4、或C2与C3、或C1与C4)均在多边形内则为三角形。网格四等份后,c1、c2、c3、c4的坐标变成小数了,实际计算时可以将坐标值乘4(原多边形顶点坐标值也都乘4)来消除小数。

AC代码

#include <iostream>
#include <cstring>
using namespace std;

#define M 8
#define N 70
short m, n, f[M][M], ff[M][4][M][M], y[M][4], sx[M][4], sy[M][4]; bool vis[M];
struct polygon {
    short x[N], y[N], n;
    short area() const {
        short s = x[n-1]*y[0] - x[0]*y[n-1];
        for (short i=1; i<n; ++i) s += x[i-1]*y[i] - x[i]*y[i-1];
        return abs(s);
    }
    bool gs() {
        short xm = m<<1, ym = m<<1;
        for (short i=0; i<n; ++i) xm = min(xm, x[i]), ym = min(ym, y[i]);
        for (short i=0; i<n; ++i) if (max(x[i]-=xm, y[i]-=ym) > m) return true;
        return false;
    }
    bool same(const polygon& p) const {
        for (short i=0; i<n; ++i) if (x[i]!=p.x[i] || y[i]!=p.y[i]) return false;
        return true;
    }
    void rot(polygon& r) const {
        r.n = n; for (short i=0; i<n; ++i) r.x[i] = y[i], r.y[i] = -x[i];
    }
    bool cnt(short x0, short y0) const {
        short c = 0;
        for (short i=0; i<n; ++i) {
            short j = i+1==n ? 0 : i+1, x1 = x[i]<<2, y1 = y[i]<<2, x2 = x[j]<<2, y2 = y[j]<<2;
            if ((y1-y0)*(x2-x0)==(y2-y0)*(x1-x0) && (x1-x0)*(x2-x0)+(y1-y0)*(y2-y0)<0) return true;
            if (y0 > min(y1, y2) && y0 <= max(y1, y2) && x1 + (x2-x1)*(y0-y1)/(y2-y1) > x0) ++c;
        }
        return c&1;
    }
    void fill(short (&f)[M][M], short &ym, short &sx, short &sy) const {
        memset(f, 0, sizeof(f)); ym = M; sx = sy = 0;
        for (short i=0; i<m; ++i) for (short j=0; j<m; ++j) {
            if (cnt((i<<2)+2, (j<<2)+2)) {
                bool c1 = cnt((i<<2)+1, (j<<2)+2), c2 = cnt((i<<2)+2, (j<<2)+1),
                     c3 = cnt((i<<2)+3, (j<<2)+2), c4 = cnt((i<<2)+2, (j<<2)+3);
                f[i][j] = c1 && c2 && c3 && c4 ? 1 : (c1 && c2 ? 2 : (c2 && c3 ? 3 : (c1 && c4 ? 4 : 5)));
            } else f[i][j] = 0;
            if (i==0 && f[i][j] && ym==M) ym = j;
            if (f[i][j]) sx = max(sx, short(i+1)), sy = max(sy, short(j+1));
        }
    }
} p[M][4];
bool check(short x, short y, const short (&ff)[M][M], short ym, short sx, short sy) {
    for (short i=0; i<sx; ++i) for (short j=0; j<sy; ++j) if (ff[i][j]) {
        short x1 = x+i, y1 = y+j-ym;
        if ((x1>=m || y1<0 || y1>=m) || (f[x1][y1] && ff[i][j]+f[x1][y1] != 7)) return false;
    }
    return true;
}
void fill(short x, short y, const short (&ff)[M][M], short ym, short sx, short sy) {
    for (short i=0; i<sx; ++i) for (short j=0; j<sy; ++j) if (ff[i][j]) {
        short x1 = x+i, y1 = y+j-ym;
        f[x1][y1] += ff[i][j];
        if (f[x1][y1] == 7) f[x1][y1] = 1;
    }
}
void unfill(short x, short y, const short (&ff)[M][M], short ym, short sx, short sy) {
    for (short i=0; i<sx; ++i) for (short j=0; j<sy; ++j) if (ff[i][j]) {
        short x1 = x+i, y1 = y+j-ym;
        f[x1][y1] = f[x1][y1] == ff[i][j] ? 0 : 7-ff[i][j];
    }
}
bool dfs() {
    for (short x=0; x<m; ++x) for (short y=0; y<m; ++y) if (f[x][y] != 1) {
        for (short i=0; i<n; ++i) if (!vis[i]) {
            vis[i] = true;
            for (short j=0; j<4; ++j) if (p[i][j].n && check(x, y, ff[i][j], ::y[i][j], sx[i][j], sy[i][j])) {
                fill(x, y, ff[i][j], ::y[i][j], sx[i][j], sy[i][j]);
                if (dfs()) return true;
                unfill(x, y, ff[i][j], ::y[i][j], sx[i][j], sy[i][j]);
            }
            vis[i] = false;
        }
        return false;
    }
    return true;
}
bool solve() {
    short k, s = 0; cin >> n >> m;
    for (short i=0; i<n; ++i) {
        cin >> k; p[i][0].n = k;
        for (short j=0; j<k; ++j) cin >> p[i][0].x[j] >> p[i][0].y[j];
        s += p[i][0].area();
    }
    if (s !=  m*m<<1) return false;
    for (short i=0; i<n; ++i) if (p[i][0].gs()) return false;
    for (short i=1; i<n; ++i) for (short j=1; j<4; ++j) {
        p[i][j-1].rot(p[i][j]); p[i][j].gs();
        if (p[i][0].same(p[i][j])) {
            p[i][j].n = 0; break;
        }
    }
    for (short i=0; i<n; ++i) for (short j=0; j<4; ++j)
        if (p[i][j].n) p[i][j].fill(ff[i][j], y[i][j], sx[i][j], sy[i][j]);
    memset(vis, 0, sizeof(vis)); memset(f, 0, sizeof(f));
    return dfs();
}

int main() {
    short t; cin >> t;
    while (t--) cout << (solve() ? "yes" : "no") << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值