hdu2295 Radar DLX解决重复覆盖问题

Radar

题意:有N个城市,M个雷达站,K个操作员,从M个雷达站中选择K个,来覆盖所有的N城市,每个雷达有相同的覆盖半径,问:最小的覆盖半径是多少

一看题意,就知道是个最小支配集问题,最小支配集属于NP难题,找不到多项式解法,所有只能搜索,但是普通的搜索是过不了的,鉴于这种类型的题,可以用一种的特殊的结构--双向链表,于是就可以用DLX来优化这个搜索,而且这题还需要剪枝,据说是A*,表示不懂,找到模板过了。。

可以先二分距离,然后用DLX判断就行了。

 

代码
 
   
1 /* 最小支配集 */
2 #include < stdio.h >
3 #include < string .h >
4 #include < math.h >
5 #include < stdlib.h >
6   #define INF 0x3fffffff
7   #define EPS 1e-8
8   #define EP 1e-10
9   #define NN 55
10 struct POINT{
11 double x, y;
12 }f[NN], g[NN];
13 double dis[NN][NN];
14
15 int adj[NN][NN];
16 int N, M, K, head;
17
18 int R[NN * NN], L[NN * NN], U[NN * NN], D[NN * NN];
19 int C[NN * NN]; // 记录每个节点所在列
20 int cntc[NN]; // 记录每列包含的节点数
21
22 /* 删除第c列 */
23 void remove( int c){
24 int i;
25 for (i = D[c]; i != c; i = D[i]){
26 R[L[i]] = R[i];
27 L[R[i]] = L[i];
28 }
29 }
30 /* 恢复第c列 */
31 void resume( int c){
32 int i;
33 for (i = D[c]; i != c; i = D[i]){
34 R[L[i]] = i;
35 L[R[i]] = i;
36 }
37 }
38
39 int h(){
40 bool hash[NN];
41 memset(hash, 0 , sizeof (hash));
42
43 int i, j, c;
44 int ans = 0 ;
45 for (c = R[head]; c != head; c = R[c]){
46 if ( ! hash[c]){
47 ans ++ ;
48 for (i = D[c]; i != c; i = D[i]){
49 for (j = R[i]; j != i; j = R[j]){
50 hash[C[j]] = true ;
51 }
52 }
53 }
54 }
55 return ans;
56 }
57
58 /* DLX主要部分 */
59 int dfs( int k){
60 if (R[head] == head) return 1 ;
61 if (k + h() > K) return 0 ; // A*剪枝
62
63
64 int i, j, c;
65 int Min = INF;
66 for (i = R[head]; i != head; i = R[i]){
67 if (cntc[i] < Min){
68 Min = cntc[i];
69 c = i;
70 }
71 }
72 for (i = D[c]; i != c; i = D[i]){
73 remove(i);
74 for (j = R[i]; j != i; j = R[j]){
75 remove(j);
76 cntc[C[j]] -- ;
77 }
78 if (dfs(k + 1 )) return 1 ;
79 for (j = L[i]; j != i; j = L[j]){
80 resume(j);
81 cntc[j] ++ ;
82 }
83 resume(i);
84 }
85 return 0 ;
86 }
87 /* 建图 */
88 int Build(){
89 int i, j, now, pre, first;
90 head = 0 ;
91 for (j = head; j < N; j ++ ){
92 R[j] = j + 1 ;
93 L[j + 1 ] = j;
94 }
95 L[head] = j;
96 R[j] = head;
97
98 /* 列双向链表 */
99 for (j = 1 ; j <= N; j ++ ){
100 pre = j;
101 cntc[j] = 0 ;
102 for (i = 1 ; i <= M; i ++ ){
103 if (adj[i][j]){
104 now = i * N + j;
105 C[now] = j;
106 cntc[j] ++ ;
107 D[pre] = now;
108 U[now] = pre;
109 pre = now;
110 }
111 }
112 now = j;
113 D[pre] = now;
114 U[now] = pre;
115 if (cntc[j] == 0 ) return 0 ;
116 }
117 /* 行双向链表 */
118 for (i = 1 ; i <= M; i ++ ){
119 pre = first = - 1 ;
120 for (j = 1 ; j <= N; j ++ ){
121 if (adj[i][j]){
122 now = i * N + j;
123 if (pre != - 1 ){
124 R[pre] = now;
125 L[now] = pre;
126 } else {
127 first = now;
128 }
129 pre = now;
130 }
131 }
132 if (first != - 1 ){
133 now = first;
134 R[pre] = now;
135 L[now] = pre;
136 }
137 }
138 return 1 ;
139 }
140
141 /* 判断 */
142 int OK( double mid){
143 int i, j;
144 for (i = 1 ; i <= M; i ++ ){
145 for (j = 1 ; j <= N; j ++ ){
146 if (dis[i][j] - mid < EP){
147 adj[i][j] = 1 ;
148 } else adj[i][j] = 0 ;
149 }
150 }
151 if (Build())
152 return dfs( 0 );
153 else return 0 ;
154 }
155
156 /* 二分距离 */
157 double Binary(){
158 double low = 0 ;
159 double hig = 1500 ;
160 double mid, ans = - 1 ;
161 while (hig - low > EPS){
162 mid = (low + hig) / 2 ;
163 if (OK(mid)){
164 ans = mid;
165 hig = mid;
166 } else low = mid;
167 }
168 return ans;
169 }
170 /* 计算雷达i到城市j的距离 */
171 double Distance( int i, int j){
172 return sqrt((g[i].x - f[j].x) * (g[i].x - f[j].x)
173 + (g[i].y - f[j].y) * (g[i].y - f[j].y));
174 }
175 /* 初始化距离矩阵dis[M][N] */
176 void Init(){
177 int i, j;
178 for (i = 1 ; i <= M; i ++ ){
179 for (j = 1 ; j <= N; j ++ ){
180 dis[i][j] = Distance(i, j);
181 }
182 }
183 }
184 int main()
185 {
186 int T, i;
187 scanf( " %d " , & T);
188 while (T -- ){
189 scanf( " %d%d%d " , & N, & M, & K);
190 for (i = 1 ; i <= N; i ++ ){
191 scanf( " %lf%lf " , & f[i].x, & f[i].y);
192 }
193 for (i = 1 ; i <= M; i ++ ){
194 scanf( " %lf%lf " , & g[i].x, & g[i].y);
195 }
196 Init();
197 printf( " %.6lf\n " , Binary());
198 }
199 return 0 ;
200 }
201

 

 

 

转载于:https://www.cnblogs.com/ylfdrib/archive/2010/10/03/1841724.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值