#include using namespace std; using Clock = chrono::steady_clock; namespace Config { constexpr double TIME_LIMIT = 1.9; // 秒。入力・前計算・初期評価を含む constexpr double D_POWER = 2.0; // 初期配置と移動先の重み d^D_POWER constexpr double DIAGONAL_PROB = 0.75; // 他の伐採木の斜め隣に移動先を限定する確率 constexpr uint64_t SEED = 0; constexpr int SAMPLE_COUNT = 50; constexpr uint64_t SAMPLE_SEED = 0; constexpr int LOG_INTERVAL = 100; } struct Timer { Clock::time_point start = Clock::now(); double elapsed() const { return chrono::duration(Clock::now()-start).count(); } }; struct Model { int N, T; vector cell, id, dryness; vector> neighbors; vector samples, active; vector queue; Model(int n, const vector& board): N(n), id(n*n,-1) { for (int y=0; y0 ? id[(y-1)*N+x] : T, y+10 ? id[y*N+x-1] : T, x+1 draw(0,9); samples.resize(size_t(Config::SAMPLE_COUNT)*T); for (int s=0; s& cut, double& result, const Timer* timer=nullptr, double limit=0) { if (!T) { result=0; return true; } uint64_t total=0; active[T]=0; for (int s=0; selapsed()>=limit) return false; const uint8_t* sample=samples.data()+size_t(s)*T; for (int v=0; v prefix, weight; explicit WeightedSampler(const vector& dryness) { double sum=0; for (int d:dryness) { weight.push_back(pow(double(d),Config::D_POWER)); sum+=weight.back(); prefix.push_back(sum); } } int draw(mt19937_64& rng, const vector& cut) const { for (;;) { double r=uniform_real_distribution(0.0,prefix.back())(rng); int v=(int)(upper_bound(prefix.begin(),prefix.end(),r)-prefix.begin()); if (v<(int)cut.size() && !cut[v]) return v; } } int draw_diagonal(mt19937_64& rng, const Model& model, const vector& selected, int slot, const vector& cut) const { vector seen(model.T,0); vector> candidates; candidates.reserve(4*selected.size()); double sum=0; for (int i=0; i<(int)selected.size(); ++i) { if (i==slot) continue; // 移動する伐採木自身は「他の伐採木」に含めない。 int pos=model.cell[selected[i]], y=pos/model.N, x=pos%model.N; for (int dy:{-1,1}) for (int dx:{-1,1}) { int ny=y+dy, nx=x+dx; if (ny<0 || ny>=model.N || nx<0 || nx>=model.N) continue; int v=model.id[ny*model.N+nx]; if (v==model.T || cut[v] || seen[v]) continue; seen[v]=1; // 複数の伐採木に隣接しても重複して重みを加えない。 sum+=weight[v]; candidates.emplace_back(sum,v); } } if (candidates.empty()) return -1; double r=uniform_real_distribution(0.0,sum)(rng); auto it=upper_bound(candidates.begin(),candidates.end(),r, [](double x,const auto& entry) { return x>N>>K)) return 0; vector board(N); for (auto& s:board) cin>>s; Model model(N,board); mt19937_64 rng(Config::SEED); WeightedSampler sampler(model.dryness); vector cut(model.T,0); vector selected; int count=min(K,model.T); model.build(); double initial_expected=0; model.evaluate(cut,initial_expected); // 伐採前の元盤面 for (int i=0; i