题目链接
题意
有一个锯齿状的棋盘,其边界从上到下、从左到右形成阶梯,边界点数量不超过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;
}