UVa1297/LA2178 The Minimum Number of Rooks

题目链接

        本题是2000年ICPC亚洲区域赛台北赛区的题目

题意

        有一个锯齿状的棋盘,其边界从上到下、从左到右形成阶梯,边界点数量不超过100,并且每一个棋盘格的行列坐标不超过100。求最小的车数量,使得每一个棋盘格均被守护到(棋盘格所在的行或者列上有车)。

分析

        解题思路参考了Fuxey的博客。

        dp基于两点贪心:每一行至多一个车;可以按照行数越大的车列数也越大(即所有车呈现一个斜向右下的一个形状)的限制构造最优答案。

        注意,并不是所有最优解都满足这一贪心性质,但是一定可以构造这样的最优解(考虑去交换两个不符合这个贪心性质的车的列号)。

        Fuxey博主的状态转移分析比较复杂并且实现细节不好理解,下面我将给出更简明的状态定义,并且代码的时间开销略微低于Fuxey博主代码。

        d[i][j]:表示前i列前j行均被覆盖时需要的最少车数量;

        e[i][j][k]:表示前i列被覆盖,前j行被覆盖了但是前j行的[i+1,k]列没有被覆盖(欠债)时需要的最少车数量;

        f[i][j][k]:表示前j行被覆盖,前i列被覆盖了但是前i列的[j+1,k]行没有被覆盖(欠债)时需要的最少车数量;

         一共七种状态转移:d→d、d→e、d→f、e→d、e→e、f→d、f→f

        每种状态转移的约束条件就比较清晰了,这里不展开细述,可以参见上面三幅图或者下面的代码帮助理解。

AC代码

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

#define N 105
short d[N][N], e[N][N][N], f[N][N][N], x0[N], x1[N], y0[N], y1[N], x, y, xa, xb, ya, yb;

short dy(short y, short x) {
    if (y == yb-1 || x1[y] > x) return y;
    return x1[y+1] > x ? y : dy(y+1, x);
}

short rx(short x, short y) {
    if (x == xb-1 || y1[x] > y) return x;
    return y1[x+1] > y ? x : rx(x+1, y);
}

short solve() {
    short px = x, py = y; xa = x; xb = x; ya = y; yb = y;
    memset(x0, 1, sizeof(x0)); memset(x1, 0, sizeof(x1)); memset(y0, 1, sizeof(y0)); memset(y1, 0, sizeof(y1));
    while (cin>>x>>y && x) {
        if (px == x) for (short i=min(y, py), j=max(y,py); i<j; ++i) x0[i] = min(x0[i], x), x1[i] = max(x1[i], x);
        else for (short i=min(x, px), j=max(x,px); i<j; ++i) y0[i] = min(y0[i], y), y1[i] = max(y1[i], y);
        px = x; py = y; xb = max(x, xb); yb = max(y, yb);
    }
    for (short i=xa; i<px; ++i) y0[i] = ya;
    x1[ya-1] = xa; memset(e, 1, sizeof(e)); memset(f, 1, sizeof(f)); memset(d, 1, sizeof(d)); d[ya-1][xa-1] = 0;
    for (y=ya-1; y<yb; ++y) for (x=xa-1; x<x1[y]; ++x) {
        if (d[y][x] < N) {
            short v = d[y][x]+1, z = x+1;
            if (z < xb-1) {
                for (short k=y+1; k<yb && x0[k]<=z; ++k) {
                    short c = k==y+1 ? z : x1[k-1]-1;
                    if (c <= z) {
                        short &r = d[dy(k, z+1)][rx(z, k+1)]; r = min(r, v);
                    } else e[k][z][c] = min(e[k][z][c], v);
                }
                if ((z = y+1) < yb-1) for (short k=x+2; k<xb && y0[k]<=z; ++k) {
                    short c = y1[k-1]-1;
                    if (c <= z) {
                        short &r = d[dy(z, k+1)][rx(k, z+1)]; r = min(r, v);
                    } else f[z][k][c] = min(f[z][k][c], v);
                }
            } else d[yb-1][z] = min(d[yb-1][z], v);
        }
        if (y > ya) for (short k=x+1; k<x1[y-1]; ++k) if (e[y][x][k] < N) {
            short v = e[y][x][k]+1, c = x+1;
            for (short z=y+1; z<yb && x0[z]<=c; ++z) {
                short t = z==y+1 ? k : x1[z-1]-1;
                if (t <= c) {
                    short &r = d[dy(z, c+1)][rx(c, z+1)]; r = min(r, v);
                } else {
                    short &r = e[z][c][t]; r = min(r, v);
                }
            }
        }
        if (x > xa) for (short k=y+1; k<y1[x-1]; ++k) if (f[y][x][k] < N) {
            short v = f[y][x][k]+1, c = y+1;
            for (short z=x+1; z<xb && y0[z]<=c; ++z) {
                short t = z==x+1 ? k : y1[z-1]-1;
                if (t <= c) {
                    short &r = d[dy(c, z+1)][rx(z, c+1)]; r = min(r, v);
                } else {
                    short &r = f[c][z][t]; r = min(r, v);
                }
            }
        }
    }
    return d[yb-1][xb-1];
}

int main() {
    short k = 0;
    while (cin>>x>>y && x) cout << ++k << ' ' << solve() << endl;
    return 0;
}

测试数据生成代码

        另外,本题需要考虑数据的一些边界情况,所以我写了一份测试数据生成的代码,方便读者调试排错。

#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;

#define N 15
// #define N 100
short x0[N+1], x1[N+1];

void generate() {
    short xa = rand()%(N-5)+1, ya = rand()%(N-5)+1, yb = rand()%(N-ya) + ya+1;
    x0[ya] = xa; x1[ya] = rand()%(N-xa) + xa+1;
    for (short i=ya+1; i<yb; ++i) {
        x0[i] = rand()%(x1[i-1]-x0[i-1]) + x0[i-1];
        x1[i] = x1[i-1]==N ? N : rand()%(N-x1[i-1]) + x1[i-1];
    }
    x0[yb] = x0[yb-1]; x1[yb] = x1[yb-1];
    cout << ya << ' ' << xa << endl << ya << ' ' << x1[ya] << endl;
    short x = x1[ya], y = ya, r0 = ya, r1 = ya+1;
    while (x!=xa || y!=ya) {
        if (r0 < r1) {
            if (x < x1[r1]) {
                cout << r1 << ' ' << x << endl << r1 << ' ' << x1[r1] << endl;
                x = x1[r1]; y = r1;
                if (y == yb) {
                    r0 = r1; r1 = r0-1;
                } else r0 = r1++;
            } else {
                y = r1;
                if (y == yb) {
                    cout << y << ' ' << x << endl << y << ' ' << x0[y] << endl;
                    x = x0[y];
                    r0 = r1--;
                } else r0 = r1++;
            }
        } else if (x > x0[r1]) {
            cout << y << ' ' << x << endl << y << ' ' << x0[r1] << endl;
            x = x0[r1]; y = r1;
            if (y > ya) r0 = r1--;
        } else {
            y = r1;
            if (y > ya) r0 = r1--;
        }
    }
    cout << "0 0" << endl << endl;
}

int main() {
    freopen("in.txt", "w", stdout);
    srand(time(NULL));
    for (int i=0; i<600; ++i) generate();
    cout << "0 0" << endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值