解いた問題のソースコードと解説など。


SRM 474 Div2 Hard SquaresCovering

問題

2次元平面上に点が複数あり、それらの位置x[i], y[i]が与えられる。この点を複数種類の正方形で全て覆うことを考える。正方形jの幅はsides[j]で与えられる。正方形jを一つ配置するコストはcost[j]である。正方形はどの種類も無尽蔵にあるとする。全ての点を覆うコストを最小化せよ。

やりかた

practice room のOgieKakoさんのコードを参考にしました。

bitDP。dp[i]:=(点の集合iを正方形で覆うときの最小コスト)でdp。まず点集合iを一枚で覆える正方形の最小コストでdp[i]を初期化する。すると、DPは点集合iの部分集合をsubとすると

dp[i] = min(dp[i], dp[sub] + dp[i xor sub])

となる。subのループの回し方は降順でまわしていく必要がある。

たとえばi = "1101"、sub = "1100"とすると

dp[1101] = min(dp[1101], dp[1100] + dp[0001])

という感じで、部分集合のループが1100の時に下線部はすでに計算されている必要がある。昇順に列挙するとそうならない。

以下ソース。

ll dp[1 << 16];

class SquaresCovering {
public:
  int minCost(vector <int> x, vector <int> y, vector <int> cost, vector <int> sides) {
    int N = x.size();
    fill(dp, dp + (1 << N), INF);
    for(int S = 0; S < 1 << N; S++){
      ll x1 = INF, x2 = -INF, y1 = INF, y2 = -INF;
      for(int i = 0; i < N; i++)
	if(S >> i & 1){
	  x1 = min(x1, (ll)x[i]); x2 = max(x2, (ll)x[i]);
	  y1 = min(y1, (ll)y[i]); y2 = max(y2, (ll)y[i]);
	}
      
      //ここではdp[S]はSの点集合を一枚で覆える正方形の最小コストになる
      //一枚で覆えないならINF
      ll d = max(x2 - x1, y2 - y1);
      for(int i = 0; i < (int)cost.size(); i++)
	if(sides[i] >= d) dp[S] = min(dp[S], (ll)cost[i]);
      
      for(int sub = S; sub > 0; sub = (sub - 1) & S)
	dp[S] = min(dp[S], dp[sub] + dp[S ^ sub]);
    }  
    
    return dp[(1 << N) - 1];
  }
};

bitDPは苦手だ。このブログでも何回かやってみたけどまだ慣れない。

ちなみにSの部分集合の降順列挙は

for(int sub = S; sub > 0; sub = (sub - 1) & S)
Get up! 明日のSUPER ST@R!