题意:按照顺时针给出操场的周边点,然后给出周围可以建设照明灯的位置,以及在该位置建设照明灯的代价,照明灯照射的范围与操场的边界相切,现在要求一个最小的花费,要求操场的所有边都被照射到。
解题关键:预处理每台灯能够覆盖到的范围,然后对环进行dp即可。对环进行dp的方法是枚举起点,覆盖所有点即可。
注意用叉积的方法处理灯能否照到某条边->某个点。
$dp[i][j]$表示从第$i$个点到第$j$个点之间的边都被照射到的最小代价,只要有某个等得照射范围有覆盖到$i$,$j$,就可以向外扩展。
#include<bits/stdc++.h> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const double eps=1e-8; const int N=105; const int M=1005; int n,m,dp[N]; bool flag[N]; struct Point{ double x,y; Point(double x=0,double y=0) { this->x=x; this->y=y; } void read(){ scanf("%lf%lf",&x,&y); } }p[N],o; struct node{ int l,r,c; }q[M]; bool judge(Point p0, Point p1, Point p2) { return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y)<-eps;//叉积判断是否能被照到 } node tra(Point t, int c){ node ans; ans.c=c; memset(flag,0,sizeof flag); for(int i=0;i<n;i++) if(judge(t,p[i],p[i+1])) flag[i]=true; if (flag[0]&&flag[n-1]){ int l=n-1,r=0; while(flag[l-1]) l--; while(flag[r+1]) r++; ans.l=l,ans.r=r+n; } else{ int l=0,r=n-1; while(!flag[l]) l++; while(!flag[r]) r--; ans.l=l,ans.r=r; } return ans; } bool solve(){ int ans=inf; for(int i=0;i<n;i++){ fill(dp,dp+2*n+1,inf); dp[i]=0; for(int j=0;j<n;j++){ int r=i+j; for(int k=0;k<m;k++){ if(q[k].l>r) continue; int now=min(i+n,q[k].r+1); dp[now]=min(dp[now],dp[r]+q[k].c); } } ans=min(ans,dp[i+n]); } if(ans==inf) return false; printf("%d ",ans); return true; } int main(){ while(~scanf("%d",&n)&&n){ for(int i=0;i<n;i++) p[i].read(); p[n]=p[0]; scanf("%d",&m); Point tmp; int c; for(int i=0;i<m;i++){ tmp.read(); scanf("%d",&c); q[i]=tra(tmp,c); } if (!solve()) printf("Impossible. "); } return 0; }