AtCoder abc274 参加メモ

キーエンスプログラミングコンテスト2022(AtCoder Beginner Contest 274) - AtCoder

B - Line Sensor

二重ループでカウント

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<(n);i++)
#define endl '\n'

int main() {
  int h,w; cin >> h >> w;
  vector<string> c(h);
  REP(i,h) cin >> c[i];

  REP(j,w) {
    int x = 0;
    REP(i,h) { if (c[i][j] == '#') x++; }
    cout << x << " ";
  }
  cout << endl;
  return 0;
}

C - Ameba

グラフにしてdfsで親からの距離を計算

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<(n);i++)
#define endl '\n'
const int INF = numeric_limits<int>::max();

int n;
vector<int> a(2e5);
vector<vector<int>> g(4e5+1);
vector<int> dist(4e5+1,INF);

void dfs(int i, int p) {
  for(int v: g[i]) {
    if (v == p) continue;
    dist[v] = dist[i]+1;
    dfs(v,i);
  }
  return;
}

int main() {
  cin >> n;
  REP(i,n) cin >> a[i];

  REP(i,n) {
    g[a[i]].push_back((i+1)*2);
    g[a[i]].push_back((i+1)*2+1);
  }

  dist[1] = 0;
  dfs(1,-1);

  for(int k = 1; k <= 2*n+1; k++) cout << dist[k] << endl;
  return 0;
}

D - Robot Arms 2

x方向とy方向を独立して考えるDP。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<(n);i++)
#define endl '\n'

int main() {
  int n,x,y; cin >> n >> x >> y;
  vector<int> a(n);
  REP(i,n) cin >> a[i];

  set<int> sx,sy;
  sx.insert(a[0]);
  sy.insert(0);
  for(int i = 1; i < n; i++) {
    set<int> t,p;
    i % 2 == 0 ? swap(p,sx) : swap(p,sy);
    for(int v: p) {
      t.insert(v+a[i]);
      t.insert(v-a[i]);
    }
    i % 2 == 0 ? swap(t,sx) : swap(t,sy);
  }

  cout << (sx.count(x) && sy.count(y) ? "Yes" : "No") << endl;
  return 0;
}

E - Booster

巡回セールスマン問題の応用。(参考問題: C - 巡回セールスマン問題)

  • dp[n][i] := すでに訪れた集合が n であって、最後にいる場所が i であるときの、合計コストの最小値
  • 宝箱も巡回する場所の一部として一緒に考えて良い。
#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<(n);i++)
#define endl '\n'
using ll = long long;
const ll LINF = numeric_limits<ll>::max();

int main() {
  int n,m; cin >> n >> m;
  n+=1; // 原点分を追加
  int w = n+m;
  vector<int> x(w),y(w);
  for(int i = 1; i < w; i++) cin >> x[i] >> y[i];
  vector dist(w,vector<double>(w)); // dist[i][j] := iからjへの距離
  REP(i,w) REP(j,w) dist[i][j] = sqrt(powl(x[i]-x[j],2) + powl(y[i]-y[j],2));

  // dp[n][i] := すでに訪れた集合が n であって、最後にいる場所が i であるときの、合計コストの最小値
  vector dp(1<<w, vector<double>(w,LINF));
  dp[0][0] = 0;

  REP(bit, 1<<w) {
    int cnt = 0; // bitの状態で取得している宝箱数
    REP(i,m) if (bit>>(n+i) & 1) cnt++;

    // 各bitの状態でiからjへの遷移を考える
    REP(i,w) {
      if (dp[bit][i] == LINF) continue;
      REP(j,w) {
        if (bit >> j & 1) continue;
        int nb = bit | 1<<j;
        dp[nb][j] = min(dp[nb][j], dp[bit][i] + dist[i][j]/(1<<cnt));
      }
    }
  }

  double ans = LINF;
  int t = (1<<n)-1;
  REP(bit,1<<w) if ((bit&t)==t) ans = min(ans,dp[bit][0]);
  printf("%.10f\n",ans);
  return 0;
}