本题目是给定一个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; }