#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1000;
int H, W;
struct BipartiteMatching {
  static const int MAX_V = MAX_N * MAX_N;
  int V, E;
  vector<int> graph[MAX_V];
  int match[MAX_V * 8];
  int level[MAX_V * 8 + 1];
  bool visited[MAX_V * 8];
  bool matched[MAX_V * 8];
  void clear() {
    for (int i = 0; i < MAX_V; i++) {
      graph[i].clear();
    }
  }
  void add_edge(int u, int v) {
    graph[v].push_back(u);
    graph[u].push_back(v);
  }
  bool augment(int left) {
    if (left == V) {
      return true;
    }
    if (visited[left]) {
      return false;
    }
    visited[left] = true;
    for (int i = 0, ed = graph[left].size(); i < ed; i++) {
      int right = graph[left][i];
      int next = match[right];
      if (level[next] > level[left] && augment(next)) {
        match[right] = left;
        return true;
      }
    }
    return false;
  }
  int bipartite_matching() {
    int ret = 0;
    fill(match, match + E, V);
    fill(matched, matched + V, false);
    bool cont = false;
    do {
      memset(level, -1, sizeof(level));
      level[V] = V;
      queue<int> que;
      for (int left = 0; left < V; left++) {
        if (!matched[left]) {
          que.push(left);
          level[left] = 0;
        }
      }
      while (!que.empty()) {
        int left = que.front();
        que.pop();
        for (int i = 0, ed = graph[left].size(); i < ed; i++) {
          int right = graph[left][i];
          int next = match[right];
          if (level[next] < 0) {
            level[next] = level[left] + 1;
            que.push(next);
          }
        }
      }
      memset(visited, false, sizeof(visited));
      cont = false;
      for (int left = 0; left < V; left++) {
        if (!matched[left] && augment(left)) {
          matched[left] = cont = true;
        }
      }
    } while (cont);
    return count(matched, matched + V, true) / 2;
  }
  void set_V(int V_) { V = V_; }
  void set_E(int E_) { E = E_; }
};
BipartiteMatching bm;
void init() { scanf("%d%d", &H, &W); }
inline int get_idx(int h, int w) { return h * W + w; }
const int dy[] = {1, 2, -1, -2}, dx[] = {2, 1, 2, 1};
int solve() {
  bm.set_V(H * W);
  int e = 0;
  for (int i = 0; i < H; i++) {
    for (int j = 0; j < W; j++) {
      for (int k = 0; k < 4; k++) {
        int ny = i + dy[k], nx = j + dx[k];
        if (0 <= ny && ny < H && 0 <= nx && nx < W) {
          bm.add_edge(get_idx(i, j), get_idx(ny, nx));
          e += 2;
        }
      }
    }
  }
  bm.set_E(e);
  return H * W - bm.bipartite_matching();
}
int main() {
  init();
  printf("%d\n", solve());
  return 0;
}
