AtCoderコンテスト328の問題解説

A: 閾値以下の合計

数値リストから指定された閾値以下の要素の合計を算出します。

#include <iostream>
#include <vector>
using namespace std;

int main() {
  int num, threshold;
  cin >> num >> threshold;
  vector<int> values(num);
  
  int total = 0;
  for (int &val : values) {
    cin >> val;
    if (val <= threshold) total += val;
  }
  
  cout << total << endl;
  return 0;
}

B: ゾロ目の日数

月と日が同じ数字で構成される日(例:11/11)の総数を計算します。

#include <iostream>
#include <vector>
using namespace std;

int main() {
  int months;
  cin >> months;
  vector<int> days(months + 1);
  
  int count = 0;
  for (int i = 1; i <= months; i++) {
    cin >> days[i];
    int base = i % 10;
    
    if (i < 10) {
      int current = base;
      while (current <= days[i]) {
        count++;
        current = current * 10 + base;
      }
    } else if (i % 11 == 0) {
      int current = base;
      while (current <= days[i]) {
        count++;
        current = current * 10 + base;
      }
    }
  }
  
  cout << count << endl;
  return 0;
}

C: 連続文字のカウント

隣接する同一文字の出現回数を前処理し、累積和を用いてクエリに回答します。

#include <iostream>
#include <vector>
using namespace std;

int main() {
  int len, queryCount;
  string str;
  cin >> len >> queryCount >> str;
  
  vector<int> prefix(len + 1, 0);
  for (int i = 1; i < len; i++) {
    prefix[i+1] = prefix[i] + (str[i] == str[i-1] ? 1 : 0);
  }
  
  while (queryCount--) {
    int left, right;
    cin >> left >> right;
    cout << prefix[right] - prefix[left] << '\n';
  }
  
  return 0;
}

D: ABC削除処理

スタックを用いて文字列から連続する"ABC"を効率的に削除します。

#include <iostream>
#include <stack>
#include <string>
using namespace std;

int main() {
  string input;
  cin >> input;
  stack<char> charStack;
  
  for (char c : input) {
    charStack.push(c);
    if (charStack.size() >= 3) {
      char c3 = charStack.top(); charStack.pop();
      char c2 = charStack.top(); charStack.pop();
      char c1 = charStack.top(); charStack.pop();
      
      if (c1 != 'A' || c2 != 'B' || c3 != 'C') {
        charStack.push(c1);
        charStack.push(c2);
        charStack.push(c3);
      }
    }
  }
  
  string result;
  while (!charStack.empty()) {
    result = charStack.top() + result;
    charStack.pop();
  }
  
  cout << result << endl;
  return 0;
}

E: モジュロ最小全域木

辺の組み合わせを全探索し、Union-Findで木を判定しながらモジュロ最小コストを求めます。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Edge {
  int from, to, weight;
};

struct UnionFind {
  vector<int> parent;
  UnionFind(int size) {
    parent.resize(size);
    for (int i = 0; i < size; i++) parent[i] = i;
  }
  
  int find(int x) {
    return (x == parent[x]) ? x : (parent[x] = find(parent[x]));
  }
  
  bool unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return false;
    parent[y] = x;
    return true;
  }
};

int main() {
  int nodes, edges, mod;
  cin >> nodes >> edges >> mod;
  
  vector<Edge> edgeList(edges);
  for (int i = 0; i < edges; i++) {
    cin >> edgeList[i].from >> edgeList[i].to >> edgeList[i].weight;
    edgeList[i].weight %= mod;
  }
  
  vector<int> selection(edges, 0);
  fill(selection.end() - (nodes - 1), selection.end(), 1);
  
  int minCost = mod;
  do {
    UnionFind uf(nodes);
    int sum = 0, count = 0;
    
    for (int i = 0; i < edges; i++) {
      if (selection[i]) {
        if (!uf.unite(edgeList[i].from - 1, edgeList[i].to - 1)) break;
        sum = (sum + edgeList[i].weight) % mod;
        count++;
      }
    }
    
    if (count == nodes - 1) 
      minCost = min(minCost, sum);
      
  } while (next_permutation(selection.begin(), selection.end()));
  
  cout << minCost << endl;
  return 0;
}

タグ: AtCoder C++ 累積和 最小全域木 全探索

6月25日 22:30 投稿