UVA - 10641(dp)

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



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值