LBOY: 两个题目是同一个类型,用到的解题思想都是:二分答案 + DLX 判定!以第二个题目为例来说一下。
题目中限制条件,每个消防站可以供应几户人家,如果把消防站看成是行,将用户看成是列,那么问题就是一个“重复覆盖”的模型。因此这里认为每个消防站是和用户相邻的,那么,可以认为用户就是消防站所在地儿。对于给定的最大距离,如果某消防站可以覆盖到某用户,那么就建一个节点。这样构造一个50*50的0-1矩阵。至于最大距离的判断,可以二分来枚举,最大距离,肯定是用户之间的最远距离。但是这样的枚举对于这题是过不去的,需要结构优化:
Radar 的优化没用到第一个条件,时间比较长,就补贴了。
link: http://acm.hdu.edu.cn/showproblem.php?pid=3656 http://acm.hdu.edu.cn/showproblem.php?pid=2295
#include <iostream> #include <cmath> #include <algorithm> using namespace std; #define arr 100 #define brr arr*arr #define head 0 #define inf 1 << 30 struct Point { int x, y; void in() { scanf("%d %d", &x, &y); } int dis2(const Point &p) { return (x-p.x)*(x-p.x) + (y-p.y)*(y-p.y); } }h[arr], f; struct node { int S, C; int R, L, U, D; }d[brr]; int N, M; int limit, flg, use[arr]; int dist[arr][arr]; int dis[arr*arr], dn; int H() { int ret = 0; //memset(use, 0, sizeof(use)); for (int i = 0; i <= N+1; i++) use[i] = 0; for (int t = d[head].R; t != head; t = d[t].R) { if (use[t] == false) { use[t] = true; ret ++; for (int i = d[t].D; i != t; i = d[i].D) { for (int j = d[i].R; j != i; j = d[j].R) { use[d[j].C] = true; // if (d[j].C == head) break; } } } } // printf("ret = %d\n", ret); return ret; } void remove(const int &c) { for (int i = d[c].D; i != c; i = d[i].D) { d[d[i].L].R = d[i].R; d[d[i].R].L = d[i].L; d[d[i].C].S--; } } void resume(const int &c) { for (int i = d[c].U; i != c; i = d[i].U) { d[d[i].L].R = i; d[d[i].R].L = i; d[d[i].C].S++; } } bool dfs(const int &k) { if (k + H() > M) { return false; } if (d[head].R == head) { return true; } int s = inf, c = 1; for (int t = d[head].R; t != head; t = d[t].R) { if(s > d[t].S) { s = d[t].S; c = t; } } for (int i = d[c].D; i != c; i = d[i].D) { remove(i); for (int j = d[i].R; j != i; j = d[j].R) { remove(j); } if (dfs(k + 1)) { return true; } for (int j = d[i].L; j != i; j = d[j].L) { resume(j); } resume(i); } return false; } bool check(int di) { d[head].L = N; d[head].R = 1; for (int j = 1; j <= N; j++) { d[j].S = 0; d[j].C = j; d[j].L = j - 1; d[j].R = j + 1; d[j].U = d[j].D = j; } d[N].R = head; int nod = N, one; for (int i = 1; i <= N; i++) { one = nod + 1; for (int j = 1; j <= N; j++) { if (dist[i][j] <= di) { d[j].S++; ++nod; d[nod].C = j; d[nod].L = nod - 1; d[nod].R = nod + 1; d[nod].U = d[j].U; d[nod].D = j; d[d[j].U].D = nod; d[j].U = nod; } } if (one <= nod) { d[one].L = nod; d[nod].R = one; } } for (int i = 1; i <= N; i++) { if (d[i].S == 0) return false; } return dfs(0); } void bSearch(int mn, int mx) { int md; while(mn < mx) { md = (mn + mx) >> 1; if (check(dis[md])) { mx = md ; } else { mn = md + 1; } } printf("%lf\n", sqrt(1.0*dis[mx])); } int main() { //freopen("in.in", "r", stdin); int ca; scanf("%d", &ca); while(ca-- && scanf("%d %d", &N, &M)) { dn = 0; for (int i = 1; i <= N; i++) { h[i].in(); for (int j = 1; j < i; j++) { dist[i][j] = dist[j][i] = h[i].dis2(h[j]); dis[dn++] = dist[i][j]; } } if (M >= N) { printf("%lf\n", 0); continue; } sort(dis, dis + dn); int k = 0; for (int i = 1; i < dn; i++) { if (dis[i] != dis[k]) { dis[++k] = dis[i]; } } bSearch(0, k); } return 0; }
炸弹人游戏,在空地上放置最少的炸弹来炸掉所有的可炸的墙。
先找题目中的限制条件
上面的第二个条件是重要的,这提示我们用“重复覆盖”的方法来解决,下面要做的就是向重复模型转化了。将每个空位看成行,每个可爆破的墙看成是列。为了方便,我们可以将所有的墙进行列序优先的编号。剩下的就是“套模板”了。
link: http://acm.hdu.edu.cn/showproblem.php?pid=3529
#include <iostream> using namespace std; #define arr 225 #define brr arr*arr #define head 0 #define inf 1 << 30 struct node { int S, C; int R, L, U, D; }d[brr]; int n, m, M; char mat[16][16]; int col[16][16]; void insert(int &nod, int id) { d[++nod].C = id; d[id].S++; d[nod].L = nod - 1; d[nod].R = nod + 1; d[nod].U = d[id].U; d[nod].D = id; d[d[id].U].D = nod; d[id].U = nod; } int stp[4][2] = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}}; inline bool islegal(int x, int y) { return x >= 1 && x <= n && y >= 1 && y <= m && mat[x][y] != '*'; } void insert(int x, int y, int &nod) { int one = nod + 1, id, nx, ny; for (int k = 0; k < 4; k++) { nx = x + stp[k][0]; ny = y + stp[k][1]; id = -1; while(islegal(nx, ny) == true) { if (mat[nx][ny] == '#') { id = col[nx][ny]; break; } nx = nx + stp[k][0]; ny = ny + stp[k][1]; } if (id != -1) { insert(nod, id); // printf("(%d, %d)->(%d, %d)\n", x, y, nx, ny); } } if (one <= nod) { d[one].L = nod; d[nod].R = one; } } int limit, use[arr]; int h() { int ret = 0; memset(use, 0, sizeof(use)); for (int t = d[head].R; t != head; t = d[t].R) { if (use[t] == false) { use[t] = true; ret ++; for (int i = d[t].D; i != t; i = d[i].D) { for (int j = d[i].R; j != i; j = d[j].R) { use[d[j].C] = true; } } } } // cout << "ret = " << ret << endl; return ret; } void remove(const int &c) { for (int i = d[c].D; i != c; i = d[i].D) { d[d[i].R].L = d[i].L; d[d[i].L].R = d[i].R; } } void resume(const int &c) { for (int i = d[c].U; i != c; i = d[i].U) { d[d[i].R].L = i; d[d[i].L].R = i; } } bool dfs(const int &k) { if (k + h() >= limit) { return false; } if (d[head].R == head) { if (k < limit) limit = k; return true; } int s = inf, c; for (int t = d[head].R; t != head; t = d[t].R) { if (s > d[t].S) { s = d[t].S; c = t; } } for (int i = d[c].D; i != c; i = d[i].D) { remove(i); for (int j = d[i].R; j != i; j = d[j].R) { remove(j); } if (dfs(k + 1)) { return true; } for (int j = d[i].L; j != i; j = d[j].L) { resume(j); } resume(i); } return false; } int main() { //freopen("in.in", "r", stdin); while(scanf("%d %d", &n, &m) != EOF) { M = 0; for (int i = 1; i <= n; i++) { scanf("%s", mat[i] + 1); for (int j = 1; j <= m; j++) { if(mat[i][j] == '#') { col[i][j] = ++M; } } } d[head].L = M; d[head].R = 1; for (int j = 1; j <= M; j++) { d[j].L = j - 1; d[j].R = j + 1; d[j].U = j; d[j].D = j; d[j].C = 1; d[j].S = 0; } d[M].R = head; int nod = M; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (mat[i][j] == '.') { insert(i, j, nod); } } } // printf("nod = %d\n", nod); limit = h(); while(dfs(0) == false) limit++; printf("%d\n", limit); } return 0; }
LBOY: 问题中的限制条件:
上面的第二个条件使我们要找的,建模+(重复覆盖)。以每个位置为行(1),恶魔所在的位置(进行列序优先的编号)为列。这样得到一个(N*N)*(N*N)的0-1矩阵。
link: http://acm.fzu.edu.cn/problem.php?pid=1686
#include <iostream> using namespace std; #define arr 16 #define brr 256 #define crr arr * brr #define head 0 #define inf 1 << 30 int n, m, N, M, r, c; int mat[arr][arr]; struct node { int S, C; int L, R, U, D; }d[crr]; int limit, flg, use[brr]; int h() { int ret = 0; memset(use, 0, sizeof(use)); for (int t = d[head].R; t != head; t = d[t].R) { if (use[t] == false) { use[t] = true; ret ++; for (int i = d[t].D; i != t; i = d[i].D) { for (int j = d[i].R; j != i; j = d[j].R) { use[d[j].C] = true; } } } } // printf("ret = %d\n", ret); return ret; } void remove(const int &c) { for (int i = d[c].D; i != c; i = d[i].D) { d[d[i].L].R = d[i].R; d[d[i].R].L = d[i].L; } } void resume(const int &c) { for (int i = d[c].U; i != c; i = d[i].U) { d[d[i].L].R = i; d[d[i].R].L = i; } } int dfs(const int &k) { if (k + h() > limit) { return k + h(); } if (d[head].R == head) { flg = true; return k; } int s = inf, c; for (int t = d[head].R; t != head; t = d[t].R) { if(s > d[t].S) { s = d[t].S; c = t; } } //printf("k = %d\n", k); int tmp, nxt = inf; for (int i = d[c].D; i != c; i = d[i].D) { remove(i); for (int j = d[i].R; j != i; j = d[j].R) { remove(j); } tmp = dfs(k + 1); if (flg == true) { return tmp; } nxt = min(nxt, tmp); for (int j = d[i].L; j != i; j = d[j].L) { resume(j); } resume(i); } return nxt; } int id_astar() { limit = h(); flg = false; while(flg == false) { limit = dfs(0); // printf("limit = %d\n", limit); } return limit; } int main() { while(scanf("%d %d", &n, &m) != EOF) { M = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { scanf("%d", mat[i] + j); if (mat[i][j] == 1) { mat[i][j] = -(++M); } } } scanf("%d %d", &r, &c); d[head].L = M; d[head].R = 1; for (int j = 1; j <= M; j++) { d[j].S = 0; d[j].C = j; d[j].L = j - 1; d[j].R = j + 1; d[j].U = d[j].D = j; } d[M].R = head; int nod = M, one, id; for (int i = 1; i <= n - r + 1; i++) { for (int j = 1; j <= m - c + 1; j++) { one = nod + 1; for (int p = i; p < i + r; p++) { for (int q = j; q < j + c; q++) { if (mat[p][q] < 0) { id = -mat[p][q]; d[id].S++; ++nod; d[nod].C = id; d[nod].L = nod - 1; d[nod].R = nod + 1; d[nod].U = d[id].U; d[nod].D = id; d[d[id].U].D = nod; d[id].U = nod; } } } if (one <= nod) { d[one].L = nod; d[nod].R = one; } } } printf("%d\n", id_astar()); } return 0; }
Copyright © 2007