SRM 254 DIV1 Hard SelfCatalogue
問題
「この文の中にxつのy(数字)が出てくる、またzつのw(数字)が出てくる」というような文であって、
文の中に現れるすべての数字に対して言及していて、
真であるような文を"self-catalogue"と言う。
数字はx,zの部分とy,wの部分が数えられる。x,zの部分は2桁の数ならそれぞれ数えられる。
"self-catalogue"は長さ10の数値列で表せる。インデックスiの数値は数字iが出現する回数である。ただし0の場合はその数字に言及していないことを表す。
サイズ10の数値列countsが与えられる。そのいくつかの(0つ以上の)要素は-1である。
- 1である場所にはどんな数値でも入れられ、その他の場所はcounts[i]である、辞書順最小の"self-catalogue"を表す数値列を求めよ
- -1 <= counts[i] <= 100
解答
Editorialを参考にした。
基本的は全探索。ただし各場所に入れる数値の上界で枝刈りできる。
まず明らかに22より大きい数値は入らない。
何故ならば、それぞれの場所が全部その数字を含んでいても、2桁までなので2*10。それと自分自身の数字を足しても22までにしかならないからである(22が入ることがあるとは限らない)
各場所に数値を入れていくたびに、そこに入れた数値の分だけ上界は減っていく。
なぜならば、そこに入れる分に数字が"取られる"からである。
最後には単純に判定すればいい。
他にも様々な枝刈りができるが、これで間に合う
コメント
枝刈り苦手。これは簡単なので解きたかった。
まず枝刈りしようという姿勢が無い感じ。どうやるか?というのを考えてない。それっぽかったら、考えろ
コード
#include <vector> #define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i)) #define rer(i,l,u) for(int (i)=(int)(l);(i)<=(int)(u);++(i)) using namespace std; typedef vector<int> vi; struct SelfCatalogue { vi v; bool dfs(int i, int x) { if(x < 0) return false; if(i == 10) { static int a[10]; rep(j, 10) a[j] = 0; rep(j, 10) if(v[j] != 0) { a[j] ++; a[v[j]%10] ++; if(v[j] >= 10) a[v[j]/10] ++; } rep(j, 10) if(v[j] != a[j]) return false; return true; } if(v[i] == -1) { rer(j, 0, x) { v[i] = j; if(dfs(i+1, x - j)) return true; } v[i] = -1; return false; }else { return dfs(i+1, x - v[i]); } } vector <int> construct(vector <int> counts) { v = counts; if(dfs(0, 22)) return v; else return vi(); } };