結果
| 問題 | No.5017 Tool-assisted Shooting |
| コンテスト | |
| ユーザー |
👑 |
| 提出日時 | 2023-07-16 18:58:49 |
| 言語 | C++17(gcc12) (gcc 12.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 726 ms / 2,000 ms |
| コード長 | 9,051 bytes |
| コンパイル時間 | 2,717 ms |
| コンパイル使用メモリ | 207,860 KB |
| 実行使用メモリ | 24,372 KB |
| スコア | 4,393,968 |
| 平均クエリ数 | 1000.00 |
| 最終ジャッジ日時 | 2023-07-16 19:01:48 |
| 合計ジャッジ時間 | 75,884 ms |
|
ジャッジサーバーID (参考情報) |
judge11 / judge16 |
| 純コード判定しない問題か言語 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 100 |
ソースコード
#include <bits/stdc++.h>
const int LOCAL = 0;
// clang-format off
using namespace std; using ll=long long; using ull=unsigned long long; using pll=pair<ll,ll>; const ll INF=4e18;
#define debug1(a) { cerr<<#a<<":"<<a<<endl; }
#define debug2(a,b) { cerr<<#a<<":"<<a<<" "<<#b<<":"<<b<<endl; }
#define debug3(a,b,c) { cerr<<#a<<":"<<a<<" "<<#b<<":"<<b<<" "<<#c<<":"<<c<<endl; }
#define debug4(a,b,c,d) { cerr<<#a<<":"<<a<<" "<<#b<<":"<<b<<" "<<#c<<":"<<c<<" "<<#d<<":"<<d<<endl; }
struct point {int r; int c; };
bool operator==(const point &lhs, const point &rhs) { return (lhs.r == rhs.r && lhs.c == rhs.c); }
bool operator!=(const point &lhs, const point &rhs) { return !(lhs == rhs); }
bool operator<(const point &lhs, const point &rhs) {
if (lhs.r != rhs.r){return lhs.r<rhs.r;}
return lhs.c<rhs.c;
}
point operator-(const point &self){
return {-self.r, -self.c};
}
point operator+(const point &lhs, const point &rhs){
return {lhs.r+rhs.r, lhs.c+rhs.c};
}
point operator-(const point &lhs, const point &rhs){
return lhs + (-rhs);
}
std::ostream &operator<<(std::ostream &os, point &pt) {
string s;
s = "(" + to_string(int(pt.r)) + ", " + to_string(int(pt.c)) + ")";
return os << s;
};
// clang-format on
namespace marathon {
mt19937 engine(0);
clock_t start_time;
double now() {
return 1000.0 * (clock() - start_time) / CLOCKS_PER_SEC;
}
void marathon_init() {
start_time = clock();
random_device seed_gen;
engine.seed(seed_gen());
}
int randint(int mn, int mx) {
int rng = mx - mn + 1;
return mn + (engine() % rng);
}
double uniform(double x, double y) {
const int RND = 1e8;
double mean = (x + y) / 2.0;
double dif = y - mean;
double p = double(engine() % RND) / RND;
return mean + dif * (1.0 - 2.0 * p);
}
} // namespace marathon
const int INIT_MY_C = 12;
const int H = 60;
const int W = 25;
struct enemy_t {
int init_hp;
int hp;
int power;
};
struct state_t {
enemy_t board[H][W];
int total_power;
int total_hp;
int my_c;
int turn;
int level() {
return 1 + total_power / 100;
}
};
void read_enemies(state_t &state) {
int n;
cin >> n;
for (int it = 0; it < n; it++) {
int h, p, x;
cin >> h >> p >> x;
enemy_t e;
e.hp = h;
e.init_hp = h;
e.power = p;
state.board[H - 1][x] = e;
}
}
void generate_enemies(state_t &state) {
return;
double mean = 7.5 + 0.15 * state.turn;
double sd = 1.5 + 0.03 * state.turn;
normal_distribution<> dist(mean, sd);
for (int c = 0; c < W; c++) {
double prob = 0.05; // TODO 履歴から確率を推定する
if (marathon::uniform(0.0, 1.0) < prob) {
// double h_ = dist(marathon::engine);
// int h = max(1, int(h_));
// normal_distribution<> distp(0.8 * h_, 0.1 * h_);
// double p_ = distp(marathon::engine);
// int p = max(0, int(p_));
int h = mean;
int p = 0.8 * h;
enemy_t e;
e.hp = h;
e.init_hp = h;
e.power = p;
state.board[H - 1][c] = e;
}
}
}
bool move_enemies(state_t &state) {
for (int r = 0; r < H - 1; r++) {
for (int c = 0; c < W; c++) {
state.board[r][c] = state.board[r + 1][c];
}
}
for (int c = 0; c < W; c++) {
state.board[H - 1][c] = {-1, -1, -1};
}
return state.board[0][state.my_c].hp <= 0;
}
char greedy_op(state_t &state) {
vector<double> scores(3, 0.0);
for (int pi = 0; pi <= 2; pi++) {
scores[pi] = marathon::uniform(0.01, 0.02);
int c = state.my_c;
if (pi == 0) {
c--;
} else if (pi == 2) {
c++;
}
// 盤面の外
if (c < 0 || c >= W) {
scores[pi] += -1e18;
continue;
}
// ゲームオーバー
if (state.board[0][c].hp > 0) {
scores[pi] += -1e16;
}
// 敵への攻撃
for (int r = 1; r < H; r++) {
int level = state.level();
enemy_t e = state.board[r][c];
if (e.hp <= 0) continue;
int turns = (e.hp + level - 1) / level;
if (turns > r) {
scores[pi] += -1e10 * (H - r); // 倒せるかどうか
} else {
double ratio = 1.0 * e.power / turns;
// if (state.turn > 900) ratio = 1.0 * e.init_hp / turns;
scores[pi] += ratio * 1e5;
}
break;
}
// 敵との近さ
for (int d = 0; d < W; d++) {
for (int r = 1; r < H; r++) {
int distance = abs(c - d);
enemy_t e = state.board[r][d];
if (e.hp <= 0) continue;
int level = state.level(); // 本当は途中でレベルが上がるが...
int turns = (e.hp + level - 1) / level;
if (turns + abs(c - d) > r) continue; // 倒せるかどうか
double ratio = 1.0 * e.power / turns;
// if (state.turn > 900) ratio = 1.0 * e.init_hp / turns;
scores[pi] += ratio / (distance + 1.0); // TODO 適当
}
}
}
pair<double, int> max_score = {-1e50, -1};
for (int i = 0; i <= 2; i++) {
max_score = max({scores[i], i}, max_score);
}
if (max_score.second == 0) {
return 'L';
} else if (max_score.second == 2) {
return 'R';
} else {
return 'S';
}
}
bool move_attack(state_t &state, char op) {
if (op == 'L') {
state.my_c--;
} else if (op == 'R') {
state.my_c++;
}
if (state.my_c < 0 || state.my_c >= W) return false;
if (state.board[0][state.my_c].hp > 0) return false;
for (int i = 1; i < H; i++) {
if (state.board[i][state.my_c].hp > 0) {
state.board[i][state.my_c].hp -= state.level();
// debug4(state.turn, i, state.my_c, state.level());
if (state.board[i][state.my_c].hp <= 0) {
state.total_power += state.board[i][state.my_c].power;
state.total_hp += state.board[i][state.my_c].init_hp;
state.board[i][state.my_c] = {-1, -1, -1};
}
break;
}
}
return true;
}
double simulation_score(int pi, state_t &org_state) {
double total_score = 0;
{
auto state = org_state;
{
// 初回移動
char op = 'S';
if (pi == 0) op = 'L';
if (pi == 2) op = 'R';
bool alive2 = move_attack(state, op);
if (!alive2) return -1e8;
}
int init_turn = org_state.turn;
int end_turn = 0;
if (init_turn > 200) {
end_turn = init_turn + 30;
} else {
end_turn = init_turn + 40;
}
end_turn = min(end_turn, 1000);
for (int turn = init_turn + 1; turn <= end_turn; turn++) {
state.turn = turn;
bool alive1 = move_enemies(state);
if (!alive1) {
total_score -= 1e6;
break;
}
generate_enemies(state);
char op = greedy_op(state);
bool alive2 = move_attack(state, op);
// if (init_turn > 900) {
// total_score += state.total_hp;
//} else {
total_score += state.total_power;
//}
if (!alive2) {
total_score -= 1e6;
break;
}
}
}
return total_score;
}
char select_op(state_t &state) {
int turn = state.turn;
vector<double> scores(3, 0.0);
for (int pi = 0; pi <= 2; pi++) {
scores[pi] = simulation_score(pi, state);
}
pair<double, int> max_score = {-1e50, -1};
for (int i = 0; i <= 2; i++) {
max_score = max({scores[i], i}, max_score);
}
if (max_score.second == 0) {
return 'L';
} else if (max_score.second == 2) {
return 'R';
} else {
return 'S';
}
}
int main() {
marathon::marathon_init();
if (LOCAL) {
for (int c = 0; c < W; c++) {
int p;
cin >> p;
}
}
state_t state;
{
state.total_power = 0;
state.total_hp = 0;
state.my_c = INIT_MY_C;
for (int r = 0; r < H; r++) {
for (int c = 0; c < W; c++) {
state.board[r][c] = {-1, -1, -1};
}
}
}
for (int turn = 1; turn <= 1000; turn++) {
state.turn = turn;
bool alive1 = move_enemies(state);
if (!alive1) break;
read_enemies(state);
char op = select_op(state);
cout << op << endl;
bool alive2 = move_attack(state, op);
if (!alive2) break;
}
cerr << "score==" << state.total_hp << " turn==" << state.turn << " power==" << state.total_power << " time==" << marathon::now() << endl;
return 0;
}