本题目是给定一个n(n<=30)边的凸多边形,然后给定m(m<=1000)个外围的灯的坐标和安装每个灯的费用,求用最少费用照亮所有边。
因为是凸多边形,很容易想到,把每个灯所能照亮的范围预处理成一个连续的区间,然后排序,用背包时选择就可以了。
不过有一点不同的是这里所要照亮的不是一条直线而是一个环状,所以应该从任意一个点为起始点,然后选择最优方案去包围点。
因为环状处理很经典,展示代码。
另外判断一个灯是否可以照亮一条特定的边的方法是:该灯与凸多边形的核心在边的两侧。
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define rep(i,n) for(int i=0;i<n;i++)
#define rep1(i,x,n) for(int i=x;i<=n;i++)
using namespace std;
struct point{
double x,y,val;
point(double x=0,double y=0,double z=0):x(x),y(y),val(z){}
bool operator<(const point& a)const{
if(x!=a.x) return x<a.x;
return y<a.y;
}
}col;
double cross(point a,point b){
return a.x*b.y-a.y*b.x;
}
point operator-(point a,point b){
return point(a.x-b.x,a.y-b.y);
}
int judge(point A,point a,point b){
return cross(A-a,b-a)*cross(col-a,b-a)<0;
}
const int N = 2111;
point pol[N],a[N],b[N];
int n,m;
int init(){
for(int i=1;i<=m;i++){
if(judge(a[i],pol[n],pol[n+1])){
int l=n,r=n+1;
while(judge(a[i],pol[l-1],pol[l])) l--;
while(judge(a[i],pol[r],pol[r+1])) r++;
b[i]=point(l,r,a[i].val);
}
else{
rep1(j,1,n){
if(judge(a[i],pol[j],pol[j+1])){
int l=j,r=j+1;
while(judge(a[i],pol[r],pol[r+1])) r++;
b[i]=point(l,r,a[i].val);
break;
}
}
}
}
sort(b+1,b+1+m);
}
typedef long long LL;
const LL inf = 1e12;
LL d[N][70],s,vis[N][70];
LL dp(int i,int j){
if(vis[i][j]) return d[i][j];
vis[i][j]=1;
if(j>=s+n) return d[i][j]=0;
if(i==m+1) return d[i][j]=inf;
d[i][j] = inf;
d[i][j] = min(dp(i+1,j),d[i][j]);
if(b[i].y>j && b[i].x<=j)
d[i][j] = min(d[i][j],dp(i+1,b[i].y)+(int)b[i].val);
return d[i][j];
}
int solve(){
long long res=inf;
for(int i=1;i<=n;i++){
s=i;
memset(vis,0,sizeof(vis));
res=min((LL)res,dp(1,s));
}
if(res==inf) printf("Impossible.\n");
else printf("%lld\n",res);
}
int main()
{
while(scanf("%d",&n)==1 && n){
col.x=col.y=0;
rep1(i,1,n) {
scanf("%lf %lf",&pol[i].x,&pol[i].y);
col.x+=pol[i].x;
col.y+=pol[i].y;
pol[i+n]=pol[i];
}
col.x/=n,col.y/=n;
scanf("%d",&m);
rep1(i,1,m) scanf("%lf %lf %lf",&a[i].x,&a[i].y,&a[i].val);
init();
solve();
}
return 0;
}