http://acm.pku.edu.cn/JudgeOnline/problem?id=1873
题目大意是从一个点集中选出一些点,是这些点的长度之和能够把剩下的点围起来,并且没个点有个权值,要求最后的选出的点的权值之和最小,如果有多个最小的,要求选出的点数最小。
由于点集数很小,n<=15,很容易想到枚举,每个点有选与不选2中状态,有2^n选点方式,没枚举一个选点方式,对剩下的点进行Graham扫描求凸包,看能不能满足要求。总的复杂度是O(2^n*n*logn)
一下有一些可以用到的优化手段:
1.枚举大可不必用dfs生成,可以直接用一个二进制数来表示选点集合。
2.在枚举出一个集合后,不要每次都求凸包,如果当前集合的权值之和>目前求出的最优权值之和,那就根本不用再往下面算了。
3.不要用vector来保存答案,这样肯定会超时。
#include
<
stdio.h
>
#include
<
math.h
>
const
int
MAXN
=
15
;
struct
point {
double
x, y;
} pp[MAXN
+
1
],p[MAXN
+
1
],h[MAXN
+
1
];
int
v[MAXN
+
1
],l[MAXN
+
1
];
int
ans[MAXN
+
1
],an;
double
mydistance(
const
point
&
p1,
const
point
&
p2) {
return
sqrt( (p1.x
-
p2.x)
*
(p1.x
-
p2.x)
+
(p1.y
-
p2.y)
*
(p1.y
-
p2.y));
}
double
multiply(
const
point
&
sp,
const
point
&
ep,
const
point
&
op) {
return
((sp.x
-
op.x)
*
(ep.y
-
op.y)
-
(ep.x
-
op.x)
*
(sp.y
-
op.y));
}
int
partition(point a[],
int
p,
int
r) {
int
i
=
p,j
=
r
+
1
,k;
double
ang,dis;
point R,S;
k
=
(p
+
r)
/
2
;
R
=
a[p];
a[p]
=
a[k];
a[k]
=
R;
R
=
a[p];
dis
=
mydistance(R,a[
0
]);
while
(
1
) {
while
(
1
) {
++
i;
if
(i
>
r) {
i
=
r;
break
;
}
ang
=
multiply(R,a[i],a[
0
]);
if
(ang
>
0
)
break
;
else
if
(ang
==
0
) {
if
(mydistance(a[i],a[
0
])
>
dis)
break
;
}
}
while
(
1
) {
--
j;
if
(j
<
p) {
j
=
p;
break
;
}
ang
=
multiply(R,a[j],a[
0
]);
if
(ang
<
0
)
break
;
else
if
(ang
==
0
) {
if
(mydistance(a[j],a[
0
])
<
dis)
break
;
}
}
if
(i
>=
j)
break
;
S
=
a[i];
a[i]
=
a[j];
a[j]
=
S;
}
a[p]
=
a[j];
a[j]
=
R;
return
j;
}
void
anglesort(point a[],
int
p,
int
r) {
if
(p
<
r) {
int
q
=
partition(a,p,r);
anglesort(a,p,q
-
1
);
anglesort(a,q
+
1
,r);
}
}
//
对PointSet求凸包,点数为n,凸包上的点保存在ch中,点数位len
void
Graham_scan(point PointSet[],point ch[],
int
n,
int
&
len) {
int
i,k
=
0
,top
=
2
;
point tmp;
for
(i
=
1
;i
<
n;i
++
)
if
( PointSet[i].x
<
PointSet[k].x
||
(PointSet[i].x
==
PointSet[k].x)
&&
(PointSet[i].y
<
PointSet[k].y) )
k
=
i;
tmp
=
PointSet[
0
];
PointSet[
0
]
=
PointSet[k];
PointSet[k]
=
tmp;
anglesort(PointSet,
1
,n
-
1
);
if
(n
<
3
) {
len
=
n;
for
(
int
i
=
0
;i
<
n;i
++
) ch[i]
=
PointSet[i];
return
;
}
ch[
0
]
=
PointSet[
0
];
ch[
1
]
=
PointSet[
1
];
ch[
2
]
=
PointSet[
2
];
for
(i
=
3
;i
<
n;i
++
) {
while
(multiply(PointSet[i],ch[top],ch[top
-
1
])
>=
0
) top
--
;
ch[
++
top]
=
PointSet[i];
}
len
=
top
+
1
;
}
int
main() {
int
n;
int
cases
=
0
;
while
(scanf(
"
%d
"
,
&
n)
!=
EOF
&&
n) {
cases
++
;
for
(
int
i
=
0
;i
<
n;i
++
)
scanf(
"
%lf %lf %d %d
"
,
&
pp[i].x,
&
pp[i].y,
&
v[i],
&
l[i]);
int
minv
=
1
<<
30
;
double
ex
=
0
;
for
(
int
mask
=
0
;mask
<
(
1
<<
n);mask
++
) {
int
tot
=
0
;
int
nowv
=
0
;
int
nowl
=
0
;
for
(
int
i
=
0
;i
<
n;i
++
) {
if
(mask
&
(
1
<<
i)) {
nowv
+=
v[i];
nowl
+=
l[i];
}
else
{
p[tot].x
=
pp[i].x;
p[tot].y
=
pp[i].y;
tot
++
;
}
}
if
(nowv
>
minv)
continue
;
if
(tot
==
0
||
tot
==
n)
continue
;
int
len
=
0
;
double
nl
=
0
;
Graham_scan(p,h,tot,len);
for
(
int
i
=
0
;i
<
len;i
++
)
nl
+=
mydistance(h[i],h[(i
+
1
)
%
len]);
if
(nl
<=
nowl) {
if
(nowv
<
minv
||
(nowv
==
minv
&&
n
-
tot
<
an)) {
minv
=
nowv;
an
=
0
;
for
(
int
i
=
0
;i
<
n;i
++
)
if
(mask
&
(
1
<<
i)) ans[an
++
]
=
i
+
1
;
ex
=
nowl
-
nl;
}
}
}
if
(cases
>
1
) printf(
"
\n
"
);
printf(
"
Forest %d\n
"
,cases);
printf(
"
Cut these trees:
"
);
for
(
int
i
=
0
;i
<
an;i
++
) printf(
"
%d
"
,ans[i]);
printf(
"
\n
"
);
printf(
"
Extra wood: %.2f\n
"
,ex);
}
return
0
;
}