Beautiful String

問題概要

アルファベットに1〜26の点数が付いている。与えられた文字列が最高得点になる時の得点を計算せよ。

考え方

  • 多い文字に高い得点付ければいいんじゃない?
  • 文字数カウントして個数をソートすれば大丈夫っぽい。

コード

#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

int Solve() {
  string s;
  vector<int> count(26, 0);
  getline(cin, s);
  for (int i = 0; i < s.size(); ++i) {
    if (!isalpha(s[i])) continue;
    ++count[tolower(s[i]) - 'a'];
  }
  sort(count.begin(), count.end());
  
  int cost = 0;
  for (int i = 0; i < 26; ++i) {
    cost += (i + 1) * count[i];
  }
  return cost;
}

int main(void) {
  string n_str;
  getline(cin, n_str);
  int n = atoi(n_str.c_str());
  for (int i = 0; i < n; ++i) {
    cout << "Case #" << (i + 1) << ": " << Solve() << "\n";
  }
  return 0;
}

Balanced Smileys

問題

:) やら :( やらが含まれる文字列で、カッコの対応が付いているかチェックせよ。

考え方

  • 考慮すべきカッコに種類があるわけではない。
  • Smiley が関係無い場合、カッコを潜っている段数 n を見るだけでいいテンプレ問題が有るのを思い出す。
    • 途中は n>=0 をキープし、最後に n==0 となれば OK
  • カッコの前に : があったら Smiley になる可能性があるから分岐させればいい。
  • こんなときは DP すると良さ気。
  • 最初のカッコの時だけ : チェックしない例外とか面倒だからスペース入れておこ…

コード

#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <set>
#include <string>
using namespace std;

bool Solve() {
  string line;
  getline(cin, line);
  line = ' ' + line;
  set<int> a, b;
  set<int>* st = &a, * st2 = &b;
  st->insert(0);
  for (int i = 0; i < line.size(); ++i) {
    if (line[i] != '(' && line[i] != ')')
      continue;
    st2->clear();
    set<int>::iterator itr;
    if (line[i] == '(') {
      for (itr = st->begin(); itr != st->end(); ++itr)
	st2->insert(*itr + 1);
      if (line[i - 1] == ':')
	for (itr = st->begin(); itr != st->end(); ++itr)
	  st2->insert(*itr);
    } else if (line[i] == ')') {
      for (itr = st->begin(); itr != st->end(); ++itr)
	if (*itr > 0)
	  st2->insert((*itr) - 1);
      if (line[i - 1] == ':')
	for (itr = st->begin(); itr != st->end(); ++itr)
	  st2->insert(*itr);
    }
    swap(st, st2);
  }

  return (st->find(0) != st->end());
}

int main(void) {
  string line;
  getline(cin, line);
  const int n = atoi(line.c_str());
  for (int i = 0; i < n; ++i) {
    cout << "Case #" << (i + 1) << ": "
	 << (Solve() ? "YES\n" : "NO\n");
  }
  return 0;
}

Find the Min

問題

数列の内、頭の k 項が知らされている。残りについては、"直前 k 項に現れない、最小の非負整数"という条件もわかっている。この時数列の最後 n 項目は何か。

考え方

  • 初項 k 項については乱数だけど 10^9 まである掛け算だから 64bit 使おう
  • 未知部分については、とりあえず k+1 より大きい数が現れるはずはない
  • むしろ k+1 周期でループするんじゃん。
  • まずは m[0..k] を計算しておこう。
  • [k] だけは疑似乱数の枠じゃないから別計算だな。
  • m[k+1..2k+1] を求められれば m[(n-1)%(k+1)] が答えじゃん。
  • 使える/使えない数のリストを持たないとなー。
  • 使える数は最小が云々だからsetで、使えないのは配列/vectorでいいか。

コード

#include <iostream>
#include <vector>
#include <set>
using namespace std;
typedef long long int64;

void PseudoRand(const int n, vector<int>* data) {
  int64 a, b, c, r;
  cin >> a >> b >> c >> r;
  for (int64 i = 0; i < n; ++i) {
    (*data)[i] = static_cast<int>(a);
    a = (b * a + c) % r;
  }
}

int64 Solve() {
  int n, k;
  cin >> n >> k;

  // Input and generate
  vector<int> data(k + 1);
  PseudoRand(k, &data);

  // Set up
  vector<int> used(k + 1, 0);
  for (int i = 0; i < k; ++i) {
    const int d = data[i];
    if (d <= k + 1)
      ++used[d];
  }
  set<int> usable;
  for (int i = 0; i <= k; ++i) {
    if (used[i] == 0)
      usable.insert(i);
  }

  // Set m[k]
  {
    set<int>::iterator itr = usable.begin();
    data[k] = *itr;
    ++used[*itr];
    usable.erase(itr);
  }

  // Make m[(k+1) .. 2*(k+1)-1]
  for (int i = 0; i <= k; ++i) {
    const int d = data[i];
    if (d <= k + 1 && used[d] > 0) {
      --used[d];
      if (used[d] == 0)
	usable.insert(d);
    }

    set<int>::iterator itr = usable.begin();
    data[i] = *itr;
    usable.erase(itr);
  }

  return data[(n - 1) % (k + 1)];
}


int main(void) {
  int n;
  cin >> n;
  for (int i = 0; i < n; ++i) {
    cout << "Case #" << (i + 1) << ": " << Solve() << "\n";
  }
}