TopCoder SRM 386 Div1 Medium PolygonCover

問題

n個の点が与えられる。それぞれの座標は(x[i],y[i])である。
これらの点を、いくつかの凸多角形で覆う。
それぞれの多角形は、与えられた点を結んだものでなくてはならない。


全ての点を覆うために必要な凸多角形の面積の和を求めよ。
面積A,Bの二つの多角形が重なっているときも、面積の和はA+Bと数えるものとする。

制約条件

n≦15

方針

点の集合を一つ決めると、
それを覆うために必要な最も小さい多角形は、それらの集合の凸包になる。


あらかじめ、全ての点の集合に対して凸包の面積を求めておく。
dp[i]を、iで表される点の集合を覆うために必要な面積の最小値とすると、
すべてのiの部分集合について、dp[i]=min(dp[j]+dp[i^j])
が成り立つので、部分集合を更新するようなDPをすればよい。


iとjに重なりがある場合あるが、それは前処理で、
集合sに対して、sを含むような集合で、sだけを囲うよりも面積が小さいものがあったら、
その面積に置き換えることをすればよい。

ソースコード

凸包、多角形の面積はspaghetti sourceの。

double dp[1<<15];

class PolygonCover {
  public:
  double getArea(vector <int> x, vector <int> y) {
    int n=x.size();
    rep(i,1<<n){
      dp[i]=INF;
      if(__builtin_popcount(i)<3)continue;
      G g;
      rep(j,n)if(i&1<<j)g.pb(P(x[j],y[j]));
      dp[i]=area2(convex_hull(g));
    }
    rep(i,1<<n)for(int j=i;j;j=j-1&i)dp[j]=min(dp[j],dp[i]);
    rep(i,1<<n)for(int j=i;j;j=j-1&i)dp[i]=min(dp[i],dp[j]+dp[i^j]);
    
    return dp[(1<<n)-1]/2;
  }
};