From http://longxiaozhi.is-programmer.com/posts/24817.html
Fire station Radar
LBOY: 两个题目是同一个类型,用到的解题思想都是:二分答案 + DLX 判定!以第二个题目为例来说一下。
题目中限制条件,每个消防站可以供应几户人家,如果把消防站看成是行,将用户看成是列,那么问题就是一个“重复覆盖”的模型。因此这里认为每个消防站是和用户相邻的,那么,可以认为用户就是消防站所在地儿。对于给定的最大距离,如果某消防站可以覆盖到某用户,那么就建一个节点。这样构造一个50*50的0-1矩阵。至于最大距离的判断,可以二分来枚举,最大距离,肯定是用户之间的最远距离。但是这样的枚举对于这题是过不去的,需要结构优化:
- 二分枚举长度的时候,枚举已经给定的用户之间的距离。(最后的最长距离也肯定在这里面)这就需要记录下任意两个用户之间的距离,进行排序之后,剔除重复的。然后该枚举长度为枚举长度的下表。
- 如果存在某一列没有消防站覆盖,即d[x].S == 0, 那么直接返回false.因为这样不满足没咧值至少为1的条件。
- 如果M >= N ,那么直接输出0.就可以了。因为每个用户都可以分配到一个用户。
Radar 的优化没用到第一个条件,时间比较长,就补贴了。
link: http://acm.hdu.edu.cn/showproblem.php?pid=3656 http://acm.hdu.edu.cn/showproblem.php?pid=2295
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
#
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
;
}
|
Bomberman - Just Search!
炸弹人游戏,在空地上放置最少的炸弹来炸掉所有的可炸的墙。
先找题目中的限制条件
- 每个炸弹可以放置在空地上,向四个方向爆破,爆破的半径是无限的。
- 每个墙可能被多个炸弹攻击。
- 所有的空地是联通的,也就是说至少存在一组可行解。
上面的第二个条件是重要的,这提示我们用“重复覆盖”的方法来解决,下面要做的就是向重复模型转化了。将每个空位看成行,每个可爆破的墙看成是列。为了方便,我们可以将所有的墙进行列序优先的编号。剩下的就是“套模板”了。
link: http://acm.hdu.edu.cn/showproblem.php?pid=3529
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
#
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
#
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
;
}
|