結果
| 問題 |
No.5005 3-SAT
|
| ユーザー |
|
| 提出日時 | 2025-04-14 02:02:47 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 1,903 ms / 2,000 ms |
| コード長 | 9,170 bytes |
| コンパイル時間 | 1,939 ms |
| コンパイル使用メモリ | 127,008 KB |
| 実行使用メモリ | 7,848 KB |
| スコア | 98,923 |
| 最終ジャッジ日時 | 2025-04-14 02:06:06 |
| 合計ジャッジ時間 | 198,572 ms |
|
ジャッジサーバーID (参考情報) |
judge2 / judge1 |
| 純コード判定しない問題か言語 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 100 |
ソースコード
#include <iostream>
#include <vector>
#include <array>
#include <algorithm>
#include <random>
#include <chrono>
#include <string>
#include <sstream>
using namespace std;
using namespace std::chrono;
// 条件1件分の情報を保持する構造体
struct Condition {
int a, b, c, p, q, r;
};
// 時間計測用クラス
class TimeKeeper {
steady_clock::time_point start_time;
public:
TimeKeeper() {
start_time = steady_clock::now();
}
// LIMIT(秒)を経過しているかどうか
bool is_time_over(double limit) {
auto now = steady_clock::now();
double elapsed = duration_cast<duration<double>>(now - start_time).count();
return elapsed >= limit;
}
double time_now() {
auto now = steady_clock::now();
return duration_cast<duration<double>>(now - start_time).count();
}
};
// Solver クラス(問題の解法を担当)
class Solver {
public:
int N, K; // 条件数とビット数
vector<Condition> X; // 条件リスト
// C[k][0/1]:k 桁目が 0/1 になった場合に影響を受ける条件のインデックスリスト
vector< array<vector<int>, 2> > C;
vector<int> indices; // ビットのランダム順序
int idx_ptr; // インデックスリストのポインタ
vector<bool> satisfied; // 各条件の成立状況
mt19937 rng; // 乱数生成器
Solver(int N_, int K_, const vector<Condition>& X_) : N(N_), K(K_), X(X_), C(K), idx_ptr(0), satisfied(N, false) {
random_device rd;
rng = mt19937(rd());
// 各ビット位置に対して、影響を受ける条件のインデックスを登録
for (int i = 0; i < N; i++) {
int a = X[i].a, b = X[i].b, c = X[i].c;
int p = X[i].p, q = X[i].q, r = X[i].r;
// 各ビット位置のインデックスが範囲内なら追加
if(a >= 0 && a < K) C[a][p].push_back(i);
if(b >= 0 && b < K) C[b][q].push_back(i);
if(c >= 0 && c < K) C[c][r].push_back(i);
}
// 0~K-1 のインデックスを生成しランダムシャッフル
indices.resize(K);
for (int i = 0; i < K; i++) {
indices[i] = i;
}
shuffle(indices.begin(), indices.end(), rng);
}
// 初期解 ans に対して各条件が成立しているかを計算
void initialize_satisfied(const vector<int>& ans) {
for (int i = 0; i < N; i++) {
const Condition& cond = X[i];
satisfied[i] = (ans[cond.a] == cond.p || ans[cond.b] == cond.q || ans[cond.c] == cond.r);
}
}
// 先頭から連続して成立している条件数を返す(start からチェック開始)
int calc_prefix_score(int start = 0) {
int sc = start;
while (sc < N && satisfied[sc])
sc++;
return sc;
}
// 全条件に対して先頭から条件成立している連続個数を計算
int calscore(const vector<int>& ans) {
int sc = 0;
for (int i = 0; i < N; i++) {
const Condition& cond = X[i];
if (ans[cond.a] == cond.p || ans[cond.b] == cond.q || ans[cond.c] == cond.r)
sc++;
else
break;
}
return sc;
}
// グリーディー法により初期解を生成する
pair<vector<int>, int> initial_solution() {
vector<int> ans(K, -1);
for (int i = 0; i < N; i++) {
const Condition& cond = X[i];
// 既に条件が成立しているならスキップ
if ((ans[cond.a] == cond.p) || (ans[cond.b] == cond.q) || (ans[cond.c] == cond.r))
continue;
if (ans[cond.a] == -1)
ans[cond.a] = cond.p;
else if (ans[cond.b] == -1)
ans[cond.b] = cond.q;
else if (ans[cond.c] == -1)
ans[cond.c] = cond.r;
}
uniform_int_distribution<int> bit_dist(0, 1);
for (int k = 0; k < K; k++) {
if (ans[k] == -1)
ans[k] = bit_dist(rng);
}
int sc = calscore(ans);
return make_pair(ans, sc);
}
// シャッフルされた indices からランダムに1要素を返す
int get_random_index() {
if (idx_ptr >= K) {
shuffle(indices.begin(), indices.end(), rng);
idx_ptr = 0;
}
return indices[idx_ptr++];
}
// 山登り法による局所探索
pair<vector<int>, int> solve(TimeKeeper& tk, double LIMIT) {
auto init = initial_solution();
vector<int> current_ans = init.first;
int current_sc = init.second;
initialize_satisfied(current_ans);
vector<int> best_ans = current_ans;
int best_sc = current_sc;
int loop = 0;
while (!tk.is_time_over(LIMIT)) {
loop++;
bool improved = false;
// K ビット全てについて反転を試行
for (int i = 0; i < K; i++) {
int k = get_random_index();
int old_val = current_ans[k];
int new_val = 1 - old_val;
current_ans[k] = new_val;
// k 番目のビットに関係する条件を取得(C[k][0] と C[k][1] の和集合)
vector<int> affected;
for (int cond_idx : C[k][0]) {
affected.push_back(cond_idx);
}
for (int cond_idx : C[k][1]) {
affected.push_back(cond_idx);
}
// 重複条件の除去(必要な場合のみ)
sort(affected.begin(), affected.end());
affected.erase(unique(affected.begin(), affected.end()), affected.end());
// ロールバック用に古い条件の成立状態を保存
vector<bool> old_sat(affected.size());
for (size_t j = 0; j < affected.size(); j++) {
old_sat[j] = satisfied[affected[j]];
}
// 影響を受けた条件のみ再評価
for (int cond_idx : affected) {
const Condition& cond = X[cond_idx];
satisfied[cond_idx] = (current_ans[cond.a] == cond.p ||
current_ans[cond.b] == cond.q ||
current_ans[cond.c] == cond.r);
}
// 反転前のスコアは先頭から current_sc 番目まで成立しているはず
// 影響を受けた条件の中で一番小さい番号と current_sc の小さい方から再評価
int candidate = current_sc;
if (!affected.empty()) {
int min_affected = *min_element(affected.begin(), affected.end());
candidate = min(current_sc, min_affected);
}
int new_sc = calc_prefix_score(candidate);
int delta = new_sc - current_sc;
if (delta >= 0) {
current_sc = new_sc;
if (current_sc > best_sc) {
cerr << "best: loop=" << loop << " current_sc=" << current_sc << "\n";
best_sc = current_sc;
best_ans = current_ans;
}
improved = true;
break; // 改善があれば内側ループを終了して外側に戻る
} else {
// 改善が見込めなかった場合、変更をロールバック
current_ans[k] = old_val;
for (size_t j = 0; j < affected.size(); j++) {
satisfied[affected[j]] = old_sat[j];
}
}
}
if (!improved) {
cerr << "not improved\n";
break;
}
}
return make_pair(best_ans, best_sc);
}
};
//
// エントリーポイント
//
int main(int argc, char* argv[]) {
bool DEBUG = false;
// コマンドライン引数 "--debug" のチェック
for (int i = 1; i < argc; i++) {
string arg = argv[i];
if (arg == "--debug") {
DEBUG = true;
}
}
TimeKeeper tk;
double LIMIT = DEBUG ? 0.5 : 1.9;
int N = 2048;
int K = 256;
vector<Condition> X;
X.reserve(N);
// 2048 行の入力を受け取る:各行は "a b c p q r"
for (int i = 0; i < N; i++) {
int a, b, c, p, q, r;
if (!(cin >> a >> b >> c >> p >> q >> r))
break;
X.push_back({a, b, c, p, q, r});
}
Solver solver(N, K, X);
auto result = solver.solve(tk, LIMIT);
vector<int> best_ans = result.first;
int sc = solver.calscore(best_ans);
// 答えは反転(末尾から出力)して1行に連結して出力
for (int i = K - 1; i >= 0; i--) {
cout << best_ans[i];
}
cout << "\n";
cerr << "SC " << sc << "\n";
return 0;
}