2010-1129
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
== 题目大意 ==
已知一些形状的俄罗斯方块,问是否能精确填充某一个尺寸的矩形,给出方案。
== 分析 ==
精确覆盖问题, Dancing Link 的典型题目类型。
题目的规模也可以用 int64 按位做普通的搜索。用这种方法时,需要特别小心判断一个形状能不能放下。
== 参考程序 ==
下面是使用 int64 普通的搜索:
{{{
#!cpp
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
typedef vector <int> VI;
vector<int> Ans[100];
bool finish;
const int MAXNODE = 11000;
const int MAXCOLUMN = 500;
const int MAXDEPTH = 100;
class DancingLink {
public:
int U[MAXNODE], D[MAXNODE], L[MAXNODE], R[MAXNODE], C[MAXNODE];
int selection[MAXDEPTH];
int column[MAXCOLUMN], rowNode[MAXCOLUMN], N[MAXCOLUMN], S[MAXCOLUMN];
int H; // Dancing Link head
int ccNode; // the number of node in Dancing Link Table
int columnSize; // the number of columns
inline void init(int columnSize0) {
H = 0;
ccNode = 1;
columnSize = columnSize0;
for (int i = 1; i <= columnSize; i++) {
column[i - 1] = C[i] = U[i] = D[i] = ccNode++;
L[i] = R[i] = S[i] = 0;
N[i] = i - 1;
}
// R
R[H] = column[0];
for (int i = 1; i < columnSize; i++) R[column[i - 1]] = column[i];
R[column[columnSize - 1]] = H;
// L
L[H] = column[columnSize - 1];
for (int i = columnSize - 1; i > 0; i--) L[column[i]] = column[i - 1];
L[column[0]] = H;
}
inline void insertRow(int rowSize, int * row) { // the column index has sorted
for (int i = 0; i < rowSize; i++) rowNode[i] = ccNode++;
// R
for (int i = 1; i < rowSize; i++) R[rowNode[i - 1]] = rowNode[i];
R[rowNode[rowSize - 1]] = rowNode[0];
// L
for (int i = rowSize - 1; i > 0; i--) L[rowNode[i]] = rowNode[i - 1];
L[rowNode[0]] = rowNode[rowSize - 1];
// C U D
for (int i = 0; i < rowSize; i++) {
C[rowNode[i]] = column[row[i]];
U[rowNode[i]] = U[column[row[i]]];
D[rowNode[i]] = column[row[i]];
D[U[rowNode[i]]] = U[D[rowNode[i]]] = rowNode[i];
S[column[row[i]]]++;
}
}
inline void coverColumn(int c) {
L[R[c]] = L[c];
R[L[c]] = R[c];
for (int i = D[c]; i != c; i = D[i]) {
for (int j = R[i]; j != i; j = R[j]) {
U[D[j]] = U[j];
D[U[j]] = D[j];
S[C[j]]--;
}
}
}
inline void uncoverColumn(int c) {
for (int i = U[c]; i != c; i = U[i]) {
for (int j = L[i]; j != i; j = L[j]) {
S[C[j]]++;
U[D[j]] = D[U[j]] = j;
}
}
L[R[c]] = R[L[c]] = c;
}
inline void search(int depth) {
if (R[H] == H) {
for (int o = 0; o < depth; o++) {
int j = selection[o];
do {
Ans[o].push_back(N[C[j]]);
// printf("%d ", N[C[j]]);
j = R[j];
} while (j != selection[o]);
// puts("");
}
finish = true;
return ;
}
// choose Column
int mmins = 1000000000;
int c;
for (int j = R[H]; j != H; j = R[j]) {
if (S[j] < mmins) {
mmins = S[j];
c = j;
}
}
coverColumn(c);
for (int r = D[c]; r != c; r = D[r]) {
selection[depth] = r;
for (int j = R[r]; j != r; j = R[j]) coverColumn(C[j]);
search(depth + 1);
if (finish) return;
for (int j = L[r]; j != r; j = L[j]) uncoverColumn(C[j]);
}
uncoverColumn(c);
}
} DL;
int N, x[100], y[100], shape[100][100][100];
int IND(int L, int W, int x, int y) {
return y + x * W;
}
void get(int now, int &x, int &y, int L, int W) {
y = now % W;
x = now / W;
}
int v[100], ans[100][100];
void solve(int L, int W) {
int columnsize = L * W + N;
DL.init(columnsize);
for (int i = 0; i < N; i++) Ans[i].clear();
for (int i = 0; i < N; i++) {
for (int x0 = 0; x0 + x[i] <= L; x0++)
for (int y0 = 0; y0 + y[i] <= W; y0++) {
int size = 0;
for (int j = 0; j < x[i]; j++)
for (int k = 0; k < y[i]; k++)
if (shape[i][j][k]) {
//v.push_back(IND(L, W, j + x0, k + y0));
v[size] = IND(L, W, j + x0, k + y0);
size++;
}
v[size] = L * W + i;
size++;
DL.insertRow(size, v);
}
}
DL.search(0);
for (int i = 0; i < N; i++)
sort(Ans[i].begin(), Ans[i].end());
for (int i = 0; i < N; i++) {
int color = Ans[i][(int)Ans[i].size() - 1] - L * W + 1;
for (int j = 0; j < (int)Ans[i].size(); j++) {
int x, y;
get(Ans[i][j], x, y, L, W);
ans[x][y] = color;
}
}
if (finish) {
printf("%d %d\n", L, W);
for (int i = 0; i < L; i++) {
for (int j = 0; j < W - 1; j++)
printf("%d ", ans[i][j]);
printf("%d\n", ans[i][W - 1]);
}
puts("");
}
}
int main() {
while (scanf("%d", &N) != EOF) {
int area = 0, L = 0, W = 0;
for (int i = 0; i < N; i++) {
scanf("%d %d", &x[i], &y[i]);
L = max(L, x[i]);
W = max(W, y[i]);
for (int j = 0; j < x[i]; j++)
for (int k = 0; k < y[i]; k++) {
scanf("%d", &shape[i][j][k]);
area += shape[i][j][k];
}
}
finish = false;
for (int l = L; l * W <= area; l++) {
if (area % l != 0) continue;
int w = area / l;
solve(l, w);
if (finish) break;
}
}
return 0;
}
}}}
题目大意
已知一些形状的俄罗斯方块,问是否能精确填充某一个尺寸的矩形,给出方案。
分析
精确覆盖问题, Dancing Link 的典型题目类型。
题目的规模也可以用 int64 按位做普通的搜索。用这种方法时,需要特别小心判断一个形状能不能放下。
参考程序
下面是使用 int64 普通的搜索:
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
typedef vector <int> VI;
vector<int> Ans[100];
bool finish;
const int MAXNODE = 11000;
const int MAXCOLUMN = 500;
const int MAXDEPTH = 100;
class DancingLink {
public:
int U[MAXNODE], D[MAXNODE], L[MAXNODE], R[MAXNODE], C[MAXNODE];
int selection[MAXDEPTH];
int column[MAXCOLUMN], rowNode[MAXCOLUMN], N[MAXCOLUMN], S[MAXCOLUMN];
int H; // Dancing Link head
int ccNode; // the number of node in Dancing Link Table
int columnSize; // the number of columns
inline void init(int columnSize0) {
H = 0;
ccNode = 1;
columnSize = columnSize0;
for (int i = 1; i <= columnSize; i++) {
column[i - 1] = C[i] = U[i] = D[i] = ccNode++;
L[i] = R[i] = S[i] = 0;
N[i] = i - 1;
}
// R
R[H] = column[0];
for (int i = 1; i < columnSize; i++) R[column[i - 1]] = column[i];
R[column[columnSize - 1]] = H;
// L
L[H] = column[columnSize - 1];
for (int i = columnSize - 1; i > 0; i--) L[column[i]] = column[i - 1];
L[column[0]] = H;
}
inline void insertRow(int rowSize, int * row) { // the column index has sorted
for (int i = 0; i < rowSize; i++) rowNode[i] = ccNode++;
// R
for (int i = 1; i < rowSize; i++) R[rowNode[i - 1]] = rowNode[i];
R[rowNode[rowSize - 1]] = rowNode[0];
// L
for (int i = rowSize - 1; i > 0; i--) L[rowNode[i]] = rowNode[i - 1];
L[rowNode[0]] = rowNode[rowSize - 1];
// C U D
for (int i = 0; i < rowSize; i++) {
C[rowNode[i]] = column[row[i]];
U[rowNode[i]] = U[column[row[i]]];
D[rowNode[i]] = column[row[i]];
D[U[rowNode[i]]] = U[D[rowNode[i]]] = rowNode[i];
S[column[row[i]]]++;
}
}
inline void coverColumn(int c) {
L[R[c]] = L[c];
R[L[c]] = R[c];
for (int i = D[c]; i != c; i = D[i]) {
for (int j = R[i]; j != i; j = R[j]) {
U[D[j]] = U[j];
D[U[j]] = D[j];
S[C[j]]--;
}
}
}
inline void uncoverColumn(int c) {
for (int i = U[c]; i != c; i = U[i]) {
for (int j = L[i]; j != i; j = L[j]) {
S[C[j]]++;
U[D[j]] = D[U[j]] = j;
}
}
L[R[c]] = R[L[c]] = c;
}
inline void search(int depth) {
if (R[H] == H) {
for (int o = 0; o < depth; o++) {
int j = selection[o];
do {
Ans[o].push_back(N[C[j]]);
// printf("%d ", N[C[j]]);
j = R[j];
} while (j != selection[o]);
// puts("");
}
finish = true;
return ;
}
// choose Column
int mmins = 1000000000;
int c;
for (int j = R[H]; j != H; j = R[j]) {
if (S[j] < mmins) {
mmins = S[j];
c = j;
}
}
coverColumn(c);
for (int r = D[c]; r != c; r = D[r]) {
selection[depth] = r;
for (int j = R[r]; j != r; j = R[j]) coverColumn(C[j]);
search(depth + 1);
if (finish) return;
for (int j = L[r]; j != r; j = L[j]) uncoverColumn(C[j]);
}
uncoverColumn(c);
}
} DL;
int N, x[100], y[100], shape[100][100][100];
int IND(int L, int W, int x, int y) {
return y + x * W;
}
void get(int now, int &x, int &y, int L, int W) {
y = now % W;
x = now / W;
}
int v[100], ans[100][100];
void solve(int L, int W) {
int columnsize = L * W + N;
DL.init(columnsize);
for (int i = 0; i < N; i++) Ans[i].clear();
for (int i = 0; i < N; i++) {
for (int x0 = 0; x0 + x[i] <= L; x0++)
for (int y0 = 0; y0 + y[i] <= W; y0++) {
int size = 0;
for (int j = 0; j < x[i]; j++)
for (int k = 0; k < y[i]; k++)
if (shape[i][j][k]) {
//v.push_back(IND(L, W, j + x0, k + y0));
v[size] = IND(L, W, j + x0, k + y0);
size++;
}
v[size] = L * W + i;
size++;
DL.insertRow(size, v);
}
}
DL.search(0);
for (int i = 0; i < N; i++)
sort(Ans[i].begin(), Ans[i].end());
for (int i = 0; i < N; i++) {
int color = Ans[i][(int)Ans[i].size() - 1] - L * W + 1;
for (int j = 0; j < (int)Ans[i].size(); j++) {
int x, y;
get(Ans[i][j], x, y, L, W);
ans[x][y] = color;
}
}
if (finish) {
printf("%d %d\n", L, W);
for (int i = 0; i < L; i++) {
for (int j = 0; j < W - 1; j++)
printf("%d ", ans[i][j]);
printf("%d\n", ans[i][W - 1]);
}
puts("");
}
}
int main() {
while (scanf("%d", &N) != EOF) {
int area = 0, L = 0, W = 0;
for (int i = 0; i < N; i++) {
scanf("%d %d", &x[i], &y[i]);
L = max(L, x[i]);
W = max(W, y[i]);
for (int j = 0; j < x[i]; j++)
for (int k = 0; k < y[i]; k++) {
scanf("%d", &shape[i][j][k]);
area += shape[i][j][k];
}
}
finish = false;
for (int l = L; l * W <= area; l++) {
if (area % l != 0) continue;
int w = area / l;
solve(l, w);
if (finish) break;
}
}
return 0;
}