题目:http://acm.fzu.edu.cn/problem.php?pid=1686
(不知道为什么每次做舞蹈链的题目就会莫名有要找很久的bug)
首先重复舞蹈链问题的解法就是最少的行的集合使得集合里的所有数字能覆盖一整行。做法是差不多不会变的了,我们需要将这些题转化为这个问题的模型。
首先舞蹈链中的列需要都被覆盖,对应的就是问题里的敌人被消灭。
所以对于有敌人的地方需要将他们看作列,没敌人的地方不能放到列上面去,那么要怎么放呢。
我们再看舞蹈链中的行,我们是需要从所有的行里面选择出加起来能覆盖一整行的行的集合,因此这里的行更多代表的是所有情况。也就是我们需要将所有攻击的情况放到行里面去。一行对应一次攻击,那么行的次数对应的也就是攻击的次数
攻击一次范围为a*b格,我们可以取左上的那点代表此次攻击,假如题目是n行m列的话,那么舞蹈链中就一共有 n-a+1 * m-b+1 行
对于同一次攻击,我们将其能攻击到的所有敌人都表示为舞蹈链中该行的1就可以了。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn1 = 17*17;
const int maxn2 = maxn1 * maxn1;
int M[maxn1][maxn1], up[maxn2], dow[maxn2], lef[maxn2], rig[maxn2], col[maxn2], row[maxn2];
//col : 每一点的列坐标 row: 每一点的横坐标
int hav[maxn2], head[maxn2];// hav:每列有多少个元素 head:每行的行头
int ans, id,flag;
void init() {
memset(head, -1, sizeof(head));
memset(hav, 0, sizeof(hav));
for (int i = 0; i <= id; i++) {
lef[i] = i - 1;
rig[i] = i + 1;
up[i] = dow[i] = i;
}
lef[0] = id;
rig[id] = 0;
}
void Connect(int x, int y) {
row[++id] = x; col[id] = y;
up[id] = up[y];
dow[up[y]] = id;
dow[id] = y;
up[y] = id;
if (head[x] == -1) head[x] = lef[id] = rig[id] = id;
else {
lef[id] = lef[head[x]];
rig[lef[head[x]]] = id;
lef[head[x]] = id;
rig[id] = head[x];
}
hav[y]++;
}
void remove(int aim) { //重复覆盖只需要删除该列就可以了
for (int i = dow[aim]; i != aim; i = dow[i]) { //从该点到下然后回到上面再回到该点的遍历方式
lef[rig[i]] = lef[i];
rig[lef[i]] = rig[i];
}
}
void resume(int aim) {
for (int i = up[aim]; i != aim; i = up[i]) {
lef[rig[i]] = i;
rig[lef[i]] = i;
}
}
bool per[maxn2];
int perdict() { //预估函数,通过估计接下来最优的情况来判断是否有必要搜索下去
int res = 0;
for (int i = rig[0]; i != 0; i = rig[i]) per[i] = 0;
for (int i = rig[0]; i != 0; i = rig[i]) {
if (!per[i]) {
per[i] = 1;
res++;
for (int j = up[i]; j != i; j = up[j]) {
for (int k = rig[j]; k != j; k = rig[k]) per[col[k]] = 1;
}
}
}
return res;
}
void dancing_link(int aim) {
if (aim + perdict() >= ans) {
return;
}
if (!rig[0]) {
ans = ans < aim ? ans : aim;
return;
}
int minn = rig[0];
for (int i = rig[0]; i != 0; i = rig[i]) {
if (hav[i] < hav[minn]) minn = i;
}
for (int i = dow[minn]; i != minn; i = dow[i]) {
remove(i);
for (int j = rig[i]; j != i; j = rig[j]) remove(j);
dancing_link(aim + 1);
for (int j = lef[i]; j != i; j = lef[j]) resume(j);
resume(i);
}
}
int main() {
int n, m;
while (cin >> n >> m) {
id = 0;
memset(M, 0, sizeof(M));
for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) {
int inp; scanf("%d", &inp);
if (inp) M[i][j] = ++id; //每个点的id值代表他在舞蹈链中的列数
}
int x, y; cin >> x >> y;
init();
for (int i = 1; i <= n - x+1; i++) {
for (int j = 1; j <= m - y+1; j++) {
for (int xx = i; xx <= i + x-1; xx++) {
for (int yy = j; yy <= j + y-1; yy++) {
if (M[xx][yy]) Connect(j + (i - 1)*(m - y + 1), M[xx][yy]);
}
}
}
}
ans = 0x3f3f3f3f;
dancing_link(0);
cout << ans << endl;
}
return 0;
}