SRM 241 DIV1 Hard PatternCutting
問題
width*heightの長方形の紙がある。
ここから与えられたパターンpatternを何枚か切り取りたい。
パターンは90°刻みで回転してもいいが、反転してはいけない。
最大で何枚切り取れるかを求めよ
- 1 <= width, height <= 6
- 1 <= |pattern|, |pattern[0]| <= 6
解答
左上が紙の半分に収まる切り方を全列挙する。
そこで、もう半分にまたがる所のビットマスクに対してmaxを取る。
次にもう半分を、初期のマスクを上記のまたがるマスクにしてビットDPする。
Editorialや他の解いてる人はもっと簡単な感じの解き方。
1*2以下のものは場合分けし、うまく枝刈りとかする感じだけどわかってない
コード
#include <string> #include <vector> #include <algorithm> #include <cstring> #define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i)) #define all(o) (o).begin(), (o).end() #define pb(x) push_back(x) #define mset(m,v) memset(m,v,sizeof(m)) using namespace std; typedef vector<int> vi; typedef long long ll; typedef vector<long long> vl; typedef vector<string> vs; int halfMax[1<<18]; struct PatternCutting { vl masks; vi ws, hs; int pn; int w, h, h2, h3; int searchHalfAll(ll mask, int y, int x, int num) { if(y == h2) { halfMax[mask] = max(halfMax[mask], num); return 1; } int ny = x == w-1 ? y+1 : y, nx = x == w-1 ? 0 : x+1; int r = 0; r += searchHalfAll(mask >> 1, ny, nx, num); rep(k, pn) { if(ws[k] <= w - x && hs[k] <= h - y && ((mask & masks[k]) == 0)) { r += searchHalfAll((mask | masks[k]) >> 1, ny, nx, num+1); } } return r; } int bitDP(int mask, int y, int x) { if(y == h3) { return 0; } int ny = x == w-1 ? y+1 : y, nx = x == w-1 ? 0 : x+1; int r = 0; r = max(r, bitDP(mask >> 1, ny, nx)); rep(k, pn) { if(ws[k] <= w - x && hs[k] <= h3 - y && ((mask & masks[k]) == 0)) { r = max(r, 1 + bitDP((mask | masks[k]) >> 1, ny, nx)); } } return r; } int getMax(int width, int height, vector <string> pattern) { w = width, h = height; int n = pattern.size(), m = pattern[0].size(); //回転したパターンを作る。実はこれが結構書けない感じだったので、しっかり覚えて素早く書けよう vector<vs> patterns(4); patterns[0].assign(n, string(m, '!')); rep(i, n) rep(j, m) patterns[0][i][j] = pattern[i][j]; patterns[1].assign(m, string(n, '!')); rep(i, n) rep(j, m) patterns[1][j][i] = pattern[n-i-1][j]; patterns[2].assign(n, string(m, '!')); rep(i, n) rep(j, m) patterns[2][i][j] = pattern[n-i-1][m-j-1]; patterns[3].assign(m, string(n, '!')); rep(i, n) rep(j, m) patterns[3][j][i] = pattern[i][m-j-1]; //重複の削除は1*1パターンとかの高速化になる sort(all(patterns)); patterns.erase(unique(all(patterns)), patterns.end()); rep(i, patterns.size()) if(h < patterns[i].size() || w < patterns[i][0].size()) { patterns.erase(patterns.begin()+i), i --; } pn = patterns.size(); //パターンのビットマスクを作る hs.clear(), ws.clear(), masks.clear(); rep(i, pn) { hs.pb(patterns[i].size()), ws.pb(patterns[i][0].size()); ll mask = 0; rep(y, patterns[i].size()) { rep(x, patterns[i][y].size()) if(patterns[i][y][x] == 'X') { mask |= 1LL << (y * w + x); } } masks.pb(mask); } //左上が紙の半分に収まる切り方を全列挙する h2 = h / 2; mset(halfMax, -1); searchHalfAll(0, 0, 0, 0); //ビットDPを組み合わせる int r = 0; h3 = h - h2; rep(i, 1<<(w*h2)) if(halfMax[i] != -1) { r = max(r, halfMax[i] + bitDP(i, 0, 0)); } return r; } };