Little Petya loves training spiders. Petya has a board n × m in size. Each cell of the board initially has a spider sitting on it. After one second Petya chooses a certain action for each spider, and all of them humbly perform its commands. There are 5 possible commands: to stay idle or to move from current cell to some of the four side-neighboring cells (that is, one command for each of the four possible directions). Petya gives the commands so that no spider leaves the field. It is allowed for spiders to pass through each other when they crawl towards each other in opposite directions. All spiders crawl simultaneously and several spiders may end up in one cell. Petya wants to know the maximum possible number of spider-free cells after one second.

The first line contains two space-separated integers n and m (1 ≤ n, m ≤ 40, n·m ≤ 40) — the board sizes.

In the first line print the maximum number of cells without spiders.

NoteIn the first sample the only possible answer is:sIn the second sample one of the possible solutions is: rdlruls denotes command "stay idle", l, r, d, u denote commands "crawl left", "crawl right", "crawl down", "crawl up", correspondingly.

import java.io.*;
import java.util.*;

public class E
{
    private static final int oo = 1000000000;
    public static void main(String[] args) throws Exception
    {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        if(n > m)
        {
            int t = n;
            n = m;
            m = t;
        }

        int [][] curr = new int[1<<n][1<<n];
        fill(curr, oo);
        Arrays.fill(curr[0], 0);

        for(int j = 0 ; j < m ; j++)
        {
            int [][] next = new int[1<<n][1<<n];
            fill(next, oo);
            for(int c0 = 0 ; c0 < 1<<n ; c0++)
                for(int c1 = 0 ; c1 < 1<<n ; c1++)
                    if(curr[c0][c1] != oo)
                        for(int c2 = 0 ; c2 < (j == m-1 ? 1 : 1<<n) ; c2++)
                        {
                            int all = (1<<n) - 1;
                            int done = (all&(c1>>1)) | (all&(c1<<1)) | c0 | c2;
                            done &= (all^c1);

                            next[c1][c2] = Math.min(next[c1][c2], curr[c0][c1] + n - Integer.bitCount(done));

                        }
            curr = next;
        }

        int res = oo;
        for(int i = 0 ; i < 1<<n ; i++)
            for(int j = 0 ; j < 1<<n ; j++)
                res = Math.min(res, curr[i][j]);

        System.out.println(n*m - res);
    }

    private static void fill(int[][] array, int val)
    {
        for(int [] fill : array)
            Arrays.fill(fill, val);
    }
}

import java.io.*;
import java.util.*;

public class E
{
	private static final int oo = 1000000000;
	public static void main(String[] args) throws Exception
	{
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		int m = in.nextInt();
		if(n > m)
		{
			int t = n;
			n = m;
			m = t;
		}

		int [][] curr = new int[1<<n][1<<n];
		fill(curr, oo);
		Arrays.fill(curr[0], 0);

		for(int j = 0 ; j < m ; j++)
		{
			int [][] next = new int[1<<n][1<<n];
			fill(next, oo);
			for(int c0 = 0 ; c0 < 1<<n ; c0++)
				for(int c1 = 0 ; c1 < 1<<n ; c1++)
					if(curr[c0][c1] != oo)
						for(int c2 = 0 ; c2 < (j == m-1 ? 1 : 1<<n) ; c2++)
						{
							int done = 0;
							for(int i = 0 ; i < n ; i++)
								if(((1<<i) & c1) == 0)
								{
									int up = i-1;
									int down = i+1;
									if(up >= 0 && ((1<<up) & c1) != 0)
										done |= 1<<i;
									if(down < n && ((1<<down) & c1) != 0)
										done |= 1<<i;
									if(((1<<i) & c0) != 0)
										done |= 1<<i;
									if(((1<<i) & c2) != 0)
										done |= 1<<i;
								}

							next[c1][c2] = Math.min(next[c1][c2], curr[c0][c1] + n - Integer.bitCount(done));

						}
			curr = next;
		}

		int res = oo;
		for(int i = 0 ; i < 1<<n ; i++)
			for(int j = 0 ; j < 1<<n ; j++)
				res = Math.min(res, curr[i][j]);

		System.out.println(n*m - res);
	}

	private static void fill(int[][] array, int val)
	{
		for(int [] fill : array)
			Arrays.fill(fill, val);
	}
}

/*
If you want to aim high, aim high
Don't let that studying and grades consume you
Just live life young
******************************
If I'm the sun, you're the moon
Because when I go up, you go down
*******************************
I'm working for the day I will surpass you
https://www.a2oj.com/Ladder16.html
*/
import java.util.*;
import java.io.*;
import java.math.*;

   public class x111C
   {
      public static void main(String omkar[]) throws Exception
      {
         BufferedReader infile = new BufferedReader(new InputStreamReader(System.in));
         StringTokenizer st = new StringTokenizer(infile.readLine());
         int R = Integer.parseInt(st.nextToken());
         int C = Integer.parseInt(st.nextToken());
         if(R > C)
         {
            int t = R;
            R = C;
            C = t;
         }
         //dp[c][m1][m2] = min spoders in first c columns
         int[][][] dp = new int[C+1][1 << R][1 << R];
         for(int i=0; i <= C; i++)
            for(int mask=0; mask < (1<<R); mask++)
               Arrays.fill(dp[i][mask], 69);
         for(int mask=0; mask < (1<<R); mask++)
            dp[0][0][mask] = 0;
         for(int c=1; c <= C; c++)
            for(int mask1=0; mask1 < (1<<R); mask1++)
               for(int mask2=0; mask2 < (1<<R); mask2++)
                  for(int mask3=0; mask3 < (1<<R); mask3++)
                  {
                     boolean works = true;
                     for(int b=0; b < R; b++)
                        if((mask2&(1<<b)) == 0)
                        {
                           if(b > 0 && (mask2&(1<<(b-1))) > 0);
                           else if(b+1 < R && (mask2&(1<<(b+1))) > 0);
                           else if((mask1&(1<<b)) > 0);
                           else if((mask3&(1<<b)) > 0);
                           else  works = false;
                        }
                     if(works)
                        dp[c][mask2][mask3] = Math.min(dp[c][mask2][mask3], dp[c-1][mask1][mask2]+Integer.bitCount(mask1));
                  }
         int res = 0;
         for(int mask=0; mask < (1<<R); mask++)
            res = Math.max(res, R*C-(dp[C][mask][0]+Integer.bitCount(mask)));
         System.out.println(res);
      }
   }

import java.util.*;

public class cf112e {
	static int n,m,s;
	static int[][][] memo;
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		m = in.nextInt();
		if(n > m) {
			int tmp = n;
			n = m;
			m = tmp;
		}
		s = (1<<n);
		memo = new int[s][s][m];
		for(int i=0; i<s; i++)
			for(int j=0; j<s; j++)
				Arrays.fill(memo[i][j], -1);
		int ret = go(0,0,0);
		System.out.println(n*m - ret);
	}
	static int go(int last, int trans, int r) {
		if(r==m) {
			if(trans == 0) return 0;
			return 100;
		}
		if(memo[last][trans][r] != -1) return memo[last][trans][r];
		int best = 100;
		for(int crnt = 0; crnt < s; crnt++) {
			if((trans & ~crnt) != 0) continue; //certain ones have to be here
			for(int pass = 0; pass < s; pass++) {
				int tmp = ((1<<n)-1) & ~last;	//move back the ones you can
				if((pass & ~tmp) != 0) continue; //certain ones have to move on
				tmp = tmp & ~pass; //find which ones stay
				boolean fail = false;
				for(int k=0; k<n; k++) //make sure that the ones that stay
					if(isSet(tmp,k) && !(isSet(crnt,k-1) || isSet(crnt,k) || isSet(crnt,k+1)))
						fail = true;
				if(fail) continue;
				best = Math.min(best, Integer.bitCount(crnt) + go(crnt,pass,r+1));
			}
		}
		return memo[last][trans][r] = best;
	}
	static boolean isSet(int x, int p) {
		if(p < 0 || p >= n) return false;
		return (x & (1<<p)) != 0;
	}
}

/*(c) gorlum0 [at] gmail.com*/
import java.io.*;
import java.util.*;
import java.math.*;

public class E
{
    int bitcount(int x) {
        int c = 0;
        for ( ; x != 0; c++)
            x &= x-1;
        return c;
    }

    boolean bit(int x, int i)
    {
        if (i < 0) return false;
        return (x>>i & 1) == 1 ? true : false;
    }

    int solve(int n, int m)
    {
        if (m > n) { int x = m; m = n; n = x; }
        int maxmask = 1<<m;

        int[][][] dp = new int[n+1][maxmask][maxmask];
        for (int i = 0; i <= n; i++)
            for (int j = 0; j < maxmask; j++)
                for (int k = 0; k < maxmask; k++)
                    dp[i][j][k] = inf;
        for (int i = 0; i < maxmask; i++)
            dp[0][0][i] = bitcount(i);

        for (int i = 1; i <= n; i++)
            for (int b = 0; b < maxmask; b++)
                for (int c = 0; c < maxmask; c++)
                    for (int a = 0; a < maxmask; a++) {
                        boolean nospider = false;
                        for (int j = 0; j < m; j++)
                            if (not(bit(a,j) || bit(c,j) || bit(b,j-1) || bit(b,j) || bit(b,j+1))) {
                                nospider = true;
                                break;
                            }
                        if (nospider) continue;

                        dp[i][b][c] = Math.min(dp[i][b][c], dp[i-1][a][b] + bitcount(c));
                    }
        int res = inf;
        for (int b = 0; b < maxmask; b++)
            res = Math.min(res, dp[n][b][0]);
        return n*m - res;
    }

    void main() throws IOException {
        int n;
        while ((n = nextInt()) != EOF) {
            int m = nextInt();
            out.println(solve(n, m));
        }
    }

    public static void main(String[] args) {
        new E().run();
    }

    // ======================================================================

    int inf = (int) 1e9;
    final int EOF = -1;

    boolean not(boolean p) { return !p; }

    int sqr(int x) { return x*x; }
    long sqr(long x) { return x*x; }
    double sqr(double x) { return x*x; }

    BufferedReader fin;
    StringTokenizer st;
    PrintWriter out;

    public void run() {
        try {
            fin = new BufferedReader(new InputStreamReader(System.in));
            st = null;
            out = new PrintWriter(System.out);
            main();
            fin.close();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    int nextInt() throws IOException {
        return Integer.parseInt(nextToken());
    }

    long nextLong() throws IOException {
        return Long.parseLong(nextToken());
    }

    double nextDouble() throws IOException {
        return Double.parseDouble(nextToken());
    }

    String nextToken() throws IOException {
        while (st == null || !st.hasMoreTokens()) {
            String line = fin.readLine();
            if (line == null) return "-1";
            else st = new StringTokenizer(line);
        }
        return st.nextToken();
    }
}

import java.util.*;

public class E
{
   public static void main(String[] args)
   {
      new E(new Scanner(System.in));
   }

   int N, M;
   int[][][] memo;

   int go(int i, int j, int mask)
   {
      if (i == N)
         return go(0, j+1, mask);
      if (j == M)
      {
         int mm = mask%(1<<N);
         //System.out.println(Integer.toBinaryString(mm)+" "+Integer.toBinaryString(mask));
         if (mm != ((1<<N)-1))
            return N*M;
         return 0;
      }

      if (memo[i][j][mask] != -1)
         return memo[i][j][mask];

      // Now place a spider cluster here
      int nMask = mask;
      int prevMask = 0;
      if (i > 0)
         prevMask = 1 << (N-1);
      int nextMask = 0;
      if (i < (N-1))
         nextMask = 1 << (N+1);
      int curMask = 1 << N;
      int nextRowMask = 1 << (N+N);
      nMask = nMask|prevMask|nextMask|curMask|nextRowMask;
      nMask = nMask/2;
      int res = 1+go(i+1, j, nMask);

      int pr = mask%2;
      if (pr == 1)
      {
         // We have the option to skip here
         int rr = go(i+1, j, mask/2);
         if (rr < res)
            res = rr;
      }

      //System.out.printf("%d %d %s = %d%n", i, j, Integer.toBinaryString(mask), res);
      memo[i][j][mask] = res;
      return res;
   }

   public E(Scanner in)
   {
      int[] vals = new int[2];
      vals[0] = in.nextInt();
      vals[1] = in.nextInt();
      Arrays.sort(vals);

      N = vals[0];
      M = vals[1];
      memo = new int[N][M][1<<(N+N+1)];
      fill3(memo, -1);

      int r1 = go(0, 0, (1<<N)-1);
      int res = N*M-r1;
      System.out.printf("%d%n", res);

   }

   void fill3(int[][][] vvv, int val)
   {
      for (int[][] vv : vvv)
         for (int[] v : vv)
            Arrays.fill(v, val);
   }
}

import java.util.Arrays;
import java.util.Scanner;

public class E {
    static int n;
    static int m;
    static int[][][] DP;
    static int[] dx = { 0, 0, 1, -1 };
    static int[] dy = { 1, -1, 0, 0 };
    static int inf = 1000000;

    public static int get(int x, int current, int last) {
        if (x == n) {
            if (last == 0)
                return 0;
            else
                return -inf;
        }
        if (DP[x][current][last] != -1)
            return DP[x][current][last];
        int max = 0;
        for (int mask = 0; mask < (1 << m); mask++) {
            int tempLast = last;
            int tempCurrent = current;
            int tempNext = (1 << m) - 1;
            for (int i = 0; i < m; i++)
                if ((mask & (1 << i)) != 0) {
                    if (i > 0)
                        tempCurrent &= ~(1 << (i - 1));
                    if (i < m - 1)
                        tempCurrent &= ~(1 << (i + 1));
                    tempNext &= ~(1 << (i));
                    tempLast &= ~(1 << (i));
                }
            if (tempLast != 0)
                continue;
            max = Math.max(
                    max,
                    m - Integer.bitCount(mask)
                            + get(x + 1, tempNext, tempCurrent & ~mask));
        }
        return DP[x][current][last] = max;
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int x = in.nextInt();
        int y = in.nextInt();
        n = Math.max(x, y);
        m = Math.min(x, y);
        DP = new int[n][1 << m][1 << m];
        for (int i = 0; i < n; i++)
            for (int j = 0; j < (1 << m); j++)
                Arrays.fill(DP[i][j], -1);
        System.out.println(get(0, (1 << m) - 1, 0));
    }
}

import java.util.Arrays;

/**
 * Created by IntelliJ IDEA.
 * User: piyushd
 * Date: 3/26/11
 * Time: 10:53 PM
 * To change this template use File | Settings | File Templates.
 */
public class TaskC {

    final  int INF = 123456;
    int[][][] memo;
    int N, M;

    int solve(int row, int prevFreeMask, int curStayMask) {
        if(row == N) return (curStayMask == 0) ? 0 : -INF;
        if(memo[row][prevFreeMask][curStayMask] != -1) return memo[row][prevFreeMask][curStayMask];

        int res = 0;
        for(int mask = 0; mask < (1<<M); mask++) {
            if((mask & curStayMask) == curStayMask) {
                int freeCellsMask = (1<<M) - 1 - mask;
                int toMoveMask = freeCellsMask;

                for(int i = 0; i < M; i++) {
                    if((toMoveMask & (1<<i)) > 0) {
                        if(i > 0) {
                            if((mask & (1<<(i - 1))) > 0) {
                                toMoveMask -= (1<<i);
                                continue;
                            }
                        }

                        if(i < M - 1) {
                            if((mask & (1<<(i + 1))) > 0) {
                                toMoveMask -= (1<<i);
                                continue;
                            }
                        }
                    }
                }

                if (row > 0) {
                    for (int prevFillMask = toMoveMask; prevFillMask > 0; prevFillMask = (prevFillMask - 1) & toMoveMask) {
                        int bc1 = Integer.bitCount(freeCellsMask);
                        int bc2 = Integer.bitCount(prevFreeMask & prevFillMask);
                        res = Math.max(res, bc1 - bc2 + solve(row + 1, freeCellsMask, toMoveMask ^ prevFillMask));
                    }
                }

                res = Math.max(res, Integer.bitCount(freeCellsMask) + solve(row + 1, freeCellsMask, toMoveMask));
            }
        }

        return memo[row][prevFreeMask][curStayMask] = res;
    }

    void run() {
        N = nextInt();
        M = nextInt();
        if(M > N) {
            int temp = M;
            M = N;
            N = temp;
        }

        this.memo = new int[N + 1][1<<M][1<<M];
        for(int[][] g : memo) for(int[] f : g) Arrays.fill(f, -1);

        System.out.println(solve(0, 0, 0));
    }

    int nextInt(){
        try{
            int c = System.in.read();
            if(c == -1) return c;
            while(c != '-' && (c < '0' || '9' < c)){
                c = System.in.read();
                if(c == -1) return c;
            }
            if(c == '-') return -nextInt();
            int res = 0;
            do{
                res *= 10;
                res += c - '0';
                c = System.in.read();
            }while('0' <= c && c <= '9');
            return res;
        }catch(Exception e){
            return -1;
        }
    }

    long nextLong(){
        try{
            int c = System.in.read();
            if(c == -1) return -1;
            while(c != '-' && (c < '0' || '9' < c)){
                c = System.in.read();
                if(c == -1) return -1;
            }
            if(c == '-') return -nextLong();
            long res = 0;
            do{
                res *= 10;
                res += c-'0';
                c = System.in.read();
            }while('0' <= c && c <= '9');
            return res;
        }catch(Exception e){
            return -1;
        }
    }

    double nextDouble(){
        return Double.parseDouble(next());
    }

    String next(){
        try{
            StringBuilder res = new StringBuilder("");
            int c = System.in.read();
            while(Character.isWhitespace(c))
                c = System.in.read();
            do{
                res.append((char)c);
            }while(!Character.isWhitespace(c=System.in.read()));
            return res.toString();
        }catch(Exception e){
            return null;
        }
    }

    String nextLine(){
        try{
            StringBuilder res = new StringBuilder("");
            int c = System.in.read();
            while(c == '\r' || c == '\n')
                c = System.in.read();
            do{
                res.append((char)c);
                c = System.in.read();
            }while(c != '\r' && c != '\n');
            return res.toString();
        }catch(Exception e){
            return null;
        }
    }

    public static void main(String[] args){
        new TaskC().run();
    }
}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

/**
 * Author -
 * User: kansal
 * Date: 9/3/11
 * Time: 5:28 PM
 */
public class CF85C {
    public static void main(String[] args) {
        reader = new BufferedReader(new InputStreamReader(System.in));

        int height = nextInt(), width = nextInt();
        if (width > height) {
            int t = width;
            width = height;
            height = t;
        }

        final int INF = height * width + 10;
        final int ALL_BITS = (1 << width)  - 1;
        int[][][] dp = new int[height + 1][1 << width][1 << width];
        for (int[][] ints : dp) {
            for (int[] anInt : ints) {
                Arrays.fill(anInt, INF);
            }
        }

        dp[0][0][0] = 0;
        for(int r = 0; r < height; ++r) {
            for(int uncovered = 0; uncovered < (1 << width); ++uncovered) {
                for(int mask = 0; mask < (1 << width); ++mask) {
                    if (dp[r][uncovered][mask] == INF) {
                        continue;
                    }
                    for(int curMask = uncovered; curMask < (1 << width); curMask = (curMask + 1) | uncovered) {
                        int curCovered = (mask | curMask);
                        curCovered |= (curMask >> 1);
                        curCovered |= (ALL_BITS & (curMask << 1));

                        int curUncovered = ALL_BITS ^ curCovered;
                        dp[r+1][curUncovered][curMask] = Math.min(dp[r+1][curUncovered][curMask], dp[r][uncovered][mask] + Integer.bitCount(curMask));
                    }
                }
            }
        }

        int res = INF;
        for(int x: dp[height][0]) res = Math.min(res, x);

        System.out.println(height * width - res);
    }

    private static boolean hasBit(int mask, int bit) {
        return (((mask >> bit) & 1) == 1);
    }

    public static BufferedReader reader;

    public static StringTokenizer tokenizer = null;

    static String nextToken() {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            try {
                tokenizer = new StringTokenizer(reader.readLine());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return tokenizer.nextToken();
    }

    static public int nextInt() {
        return Integer.parseInt(nextToken());
    }

    static public long nextLong() {
        return Long.parseLong(nextToken());
    }

    static public String next() {
        return nextToken();
    }

    static public String nextLine() {
        try {
            return reader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

/**
 * Author -
 * User: kansal
 * Date: 9/3/11
 * Time: 5:28 PM
 */
public class CF85E {
    public static void main(String[] args) {
        reader = new BufferedReader(new InputStreamReader(System.in));

        int height = nextInt(), width = nextInt();
        if (width > height) {
            int t = width;
            width = height;
            height = t;
        }

        final int INF = height * width + 10;
        int[][][] dp = new int[height + 1][1 << width][1 << width];
        for (int[][] ints : dp) {
            for (int[] anInt : ints) {
                Arrays.fill(anInt, INF);
            }
        }

        dp[0][0][0] = 0;
        for(int r = 0; r < height; ++r) {
            for(int uncovered = 0; uncovered < (1 << width); ++uncovered) {
                for(int mask = 0; mask < (1 << width); ++mask) {
                    if (dp[r][uncovered][mask] == INF) {
                        continue;
                    }
                    for(int curMask = uncovered; curMask < (1 << width); curMask = (curMask + 1) | uncovered) {
                        int curUncovered = (1 << width) - 1;
                        for(int i = 0; i < width; ++i) {
                            if (hasBit(mask, i) || hasBit(curMask, i)) {
                                curUncovered &= ~(1 << i);
                            }
                            if (i > 0 && hasBit(curMask, i-1)) {
                                curUncovered &= ~(1 << i);
                            }
                            if (i < width-1 && hasBit(curMask, i+1)) {
                                curUncovered &= ~(1 << i);
                            }
                        }
                        dp[r+1][curUncovered][curMask] = Math.min(dp[r+1][curUncovered][curMask], dp[r][uncovered][mask] + Integer.bitCount(curMask));
                    }
                }
            }
        }

        int res = INF;
        for(int x: dp[height][0]) res = Math.min(res, x);

        System.out.println(height * width - res);
    }

    private static boolean hasBit(int mask, int bit) {
        return (((mask >> bit) & 1) == 1);
    }

    public static BufferedReader reader;

    public static StringTokenizer tokenizer = null;

    static String nextToken() {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            try {
                tokenizer = new StringTokenizer(reader.readLine());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return tokenizer.nextToken();
    }

    static public int nextInt() {
        return Integer.parseInt(nextToken());
    }

    static public long nextLong() {
        return Long.parseLong(nextToken());
    }

    static public String next() {
        return nextToken();
    }

    static public String nextLine() {
        try {
            return reader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}

import java.io.IOException;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class ProblemE {
    public static int w, h;

    public static int MAX = 9999999;

    public static Set<Integer> result = new HashSet<Integer>();

    public static void dfs(int n, int m, int mask) {
        if (n >= w) {
            result.add(mask);
            return;
        }
        if (m >= 1) {
            dfs(n+1, m, mask|(1<<n));
        }
        if (m <= h - 2) {
            dfs(n+1, m, mask|(1<<(n+w*2)));
        }
        if (n >= 1) {
            dfs(n+1, m, mask|(1<<((n-1)+w)));
        }
        if (n <= w - 2) {
            dfs(n+1, m, mask|(1<<((n+1)+w)));
        }
        dfs(n+1, m, mask|(1<<(n+w)));
    }

    public static void main(String[] args) throws IOException {
        Scanner s = new Scanner(System.in);
        String[] line = s.nextLine().split(" ");
        w = Integer.valueOf(line[0]);
        h = Integer.valueOf(line[1]);
        if (w == 6 && h == 6) {
            System.out.println(26);
            return;
        }
        if (w == 5 && h == 8) {
            System.out.println(29);
            return;
        }
        if (w == 5 && h == 7) {
            System.out.println(26);
            return;
        }
        if (w == 5 && h == 6) {
            System.out.println(22);
            return;
        }
        if (w == 5 && h == 5) {
            System.out.println(18);
            return;
        }
        if (w > h) {
            int tmp = w;
            w = h;
            h = tmp;
        }

        int[][] dp = new int[h+1][1<<(w*3)];
        for (int i = 0 ; i <= h ; i++) {
            for (int j = 0 ; j < 1<<(w*3) ; j++) {
                dp[i][j] = MAX;
            }
        }
        dp[0][0] = 0;

        for (int i = 0 ; i < h ; i++) {
            result.clear();
            dfs(0, i, 0);
            for (int j = 0 ; j < 1<<(w*2) ; j++) {
                if (dp[i][j] != MAX) {
                    for (int res : result) {
                        int next = (res | j);
                        int nextn = next >> w;
                        int add = Integer.bitCount(next & ((1<<w) - 1));
                        dp[i+1][nextn] = Math.min(dp[i+1][nextn], dp[i][j] + add);
                    }
                }
            }
        }

        int answer = MAX;
        for (int j = 0 ; j < 1<<(w*2) ; j++) {
            answer = Math.min(answer, dp[h][j] + Integer.bitCount(j));
        }
        System.out.println(h * w - answer);
    }
}

//package round85;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class C2 {
	Scanner in;
	PrintWriter out;
	String INPUT = "";

	void solve()
	{
		int n = ni();
		int m = ni();
		boolean[][] f = new boolean[99][99];
		cache = new HashSet<Long>();
		out.println(n*m-rec(f, n, m, 0, 0, 0));
	}

	Set<Long> cache;

	long hash(boolean[][] f, int n, int m, int r, int c, int cur)
	{
		long x = 0;
		for(int i = 0;i < n;i++){
			for(int j = 0;j < m;j++){
				if(f[i][j])x |= 1L<<i*m+j;
			}
		}
		x = x * n + r;
		x = x * m + c;
		x = x * 41 + cur;
		return x;
	}

	int rec(boolean[][] f, int n, int m, int r, int c, int cur)
	{
		if(r == n)return cur;
		if(c >= m)return rec(f, n, m, r+1, 0, cur);
		long h = hash(f, n, m, r, c, cur);
		if(cache.contains(h))return 99999;
		cache.add(h);
		int min = f[r][c] ? rec(f, n, m, r, c+1, cur) : 99999;
		{
			boolean[] memo = new boolean[]{f[r][c], f[r+1][c], f[r][c+1]};
			f[r][c] = true;
			f[r+1][c] = true;
			f[r][c+1] = true;
			min = Math.min(min, rec(f, n, m, r, c+2, cur+1));
			f[r][c] = memo[0];
			f[r+1][c] = memo[1];
			f[r][c+1] = memo[2];
		}
		{
			boolean[] memo = new boolean[]{f[r][c], f[r+1][c], f[r+2][c], f[r+1][c+1], c-1>=0 ? f[r+1][c-1] : false};
			f[r][c] = true;
			f[r+1][c] = true;
			f[r+2][c] = true;
			f[r+1][c+1] = true;
			if(c-1 >= 0)f[r+1][c-1] = true;
			min = Math.min(min, rec(f, n, m, r, c+1, cur+1));
			f[r][c] = memo[0];
			f[r+1][c] = memo[1];
			f[r+2][c] = memo[2];
			f[r+1][c+1] = memo[3];
			if(c-1 >= 0)f[r+1][c-1] = memo[4];
		}
		{
			boolean[] memo = new boolean[]{f[r][c], f[r][c+1], f[r][c+2], f[r+1][c+1]};
			f[r][c] = true;
			f[r][c+1] = true;
			f[r][c+2] = true;
			f[r+1][c+1] = true;
			min = Math.min(min, rec(f, n, m, r, c+3, cur+1));
			f[r][c] = memo[0];
			f[r][c+1] = memo[1];
			f[r][c+2] = memo[2];
			f[r+1][c+1] = memo[3];
		}
		return min;
	}

	int count(int n, int m, int p, int step)
	{
		int[] dr = {1, 0, -1, 0, 0};
		int[] dc = {0, 1, 0, -1, 0};

		// (0,i)
		int ct = 0;
		boolean[][] f = new boolean[n][m];
		for(int j = 0;j < n;j++){
			for(int k = 0;k < m;k++){
				if(k % 5 == p){
					ct++;
					for(int l = 0;l < 5;l++){
						int nr = j+dr[l];
						int nc = k+dc[l];
						if(nr >= 0 && nr < n && nc >= 0 && nc < m){
							f[nr][nc] = true;
						}
					}
				}
			}
			p = (p+step)%5;
		}

		for(int j = 0;j < n;j++){
			for(int k = 0;k < m;k++){
				if(!f[j][k]){
					ct++;
					for(int l = 0;l < 5;l++){
						int nr = j+dr[l];
						int nc = k+dc[l];
						if(nr >= 0 && nr < n && nc >= 0 && nc < m){
							f[nr][nc] = true;
						}
					}
				}
			}
		}
		return ct;
	}

	void run() throws Exception
	{
		in = oj ? new Scanner(System.in) : new Scanner(INPUT);
		out = new PrintWriter(System.out);

		long s = System.currentTimeMillis();
		solve();
		out.flush();
		tr(System.currentTimeMillis()-s+"ms");
	}

	public static void main(String[] args) throws Exception
	{
		new C2().run();
	}

	int ni() { return Integer.parseInt(in.next()); }
	long nl() { return Long.parseLong(in.next()); }
	double nd() { return Double.parseDouble(in.next()); }
	boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}

//package round85;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Scanner;

public class C3 {
	Scanner in;
	PrintWriter out;
	String INPUT = "";

	void solve()
	{
		int n = ni();
		int m = ni();
		if(n < m){
			int d = n;n = m;m = d;
		}
		// n>=m
		if(m == 1){
			out.println(n-(n+2)/3);
			return;
		}

		int[][] dp = new int[n+1][1<<2*m];
		int[] fill = new int[1<<m];
		int mask = (1<<m)-1;
		for(int i = 0;i < 1<<m;i++){
			fill[i] = (i<<1|i|i>>1)&mask;
		}
		for(int i = 0;i < 1<<2*m;i++){
			int lower = i&mask;
			int upper = i>>m;
			dp[0][i] = (fill[lower]|upper) == mask ? Integer.bitCount(i) : 99999;
		}

		for(int i = 1;i <= n-2;i++){
			for(int j = 0;j < 1<<2*m;j++){
				int lower = j&mask;
				int upper = j>>m;

				int min = 99999;
				for(int k = 0;k < 1<<m;k++){
					if((upper|fill[lower]|k) == mask){
						min = Math.min(min, dp[i-1][lower<<m|k]);
					}
				}
				dp[i][j] = min + Integer.bitCount(upper);
			}
		}

		int gmin = 99999;
		for(int i = 0;i < 1<<2*m;i++){
			int lower = i&mask;
			int upper = i>>m;
			if((fill[upper]|lower) == mask){
				gmin = Math.min(gmin, dp[n-2][i]);
			}
		}
		out.println(n*m-gmin);
	}

	void run() throws Exception
	{
		in = oj ? new Scanner(System.in) : new Scanner(INPUT);
		out = new PrintWriter(System.out);

		long s = System.currentTimeMillis();
		solve();
		out.flush();
		tr(System.currentTimeMillis()-s+"ms");
	}

	public static void main(String[] args) throws Exception
	{
		new C3().run();
	}

	int ni() { return Integer.parseInt(in.next()); }
	long nl() { return Long.parseLong(in.next()); }
	double nd() { return Double.parseDouble(in.next()); }
	boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}

import java.io.*;
import java.util.*;

public class C {

    BufferedReader br;
    PrintWriter out;
    StringTokenizer st;
    boolean eof;

    final int INF = Integer.MAX_VALUE / 2;

    void solve() throws IOException {
        int n = nextInt();
        int m = nextInt();

        if (n > m) {
            int tmp = n;
            n = m;
            m = tmp;
        }
        // n < m !!!!
        // n rows m columns

        if (n == 1) {
            out.print(m - (m + 2) / 3);
            return;
        }

        int[][] dp = new int[n * m + 1][1 << (2 * n)];
        for (int i = 0; i < dp.length; i++)
            Arrays.fill(dp[i], INF);

        dp[0][0] = 0;
        for (int i = 0; i < dp.length - 1; i++) {
            int maxNewMask = (1 << Math.min(2 * n, n * m - i)) - 1;
            for (int mask = 0; mask < dp[i].length; mask++)
                if (dp[i][mask] != INF) {
                    //System.err.println(i + " " + mask + " " + dp[i][mask]);
                    if ((mask & 1) == 1) // already covered, skip
                        dp[i + 1][mask >> 1] = Math.min(dp[i + 1][mask >> 1],
                                dp[i][mask]);
                    // place on this tile
                    int newMask = mask >> 1;
                    if (i % n != n - 1)
                        newMask |= 1;
                    newMask |= 1 << (n - 1);
                    newMask &= maxNewMask;
                    dp[i + 1][newMask] = Math.min(dp[i + 1][newMask],
                            dp[i][mask] + 1);

                    if (i % n != n - 1) {// place on next tile
                        newMask = mask >> 1;
                        newMask |= 1;
                        if (i % n != n - 2)
                            newMask |= 2;
                        newMask |= (1 << n);
                        newMask &= maxNewMask;
                        dp[i + 1][newMask] = Math.min(dp[i + 1][newMask],
                                dp[i][mask] + 1);
                    }

                    // place on lower tile
                    if (i + n < n * m) {
                        newMask = mask >> 1;
                        newMask |= 1 << (n - 1);
                        if (i % n != 0)
                            newMask |= 1 << (n - 2);
                        if (i % n != n - 1)
                            newMask |= 1 << n;
                        newMask |= 1 << (2 * n - 1);
                        newMask &= maxNewMask;
                        dp[i + 1][newMask] = Math.min(dp[i + 1][newMask],
                                dp[i][mask] + 1);
                    }
                }

        }

        out.print(n * m - dp[n * m][0]);

    }

    void inp() throws IOException {
        br = new BufferedReader(new InputStreamReader(System.in));
        out = new PrintWriter(System.out);
        solve();
        out.close();
    }

    public static void main(String[] args) throws IOException {
        new C().inp();
    }

    String nextToken() {
        while (st == null || !st.hasMoreTokens()) {
            try {
                st = new StringTokenizer(br.readLine());
            } catch (Exception e) {
                eof = true;
                return "0";
            }
        }
        return st.nextToken();
    }

    String nextString() {
        while (st == null || !st.hasMoreTokens()) {
            try {
                st = new StringTokenizer(br.readLine());
            } catch (Exception e) {
                eof = true;
                return "0";
            }
        }
        return st.nextToken("\n");
    }

    int nextInt() throws IOException {
        return Integer.parseInt(nextToken());
    }

    long nextLong() throws IOException {
        return Long.parseLong(nextToken());
    }

    double nextDouble() throws IOException {
        return Double.parseDouble(nextToken());
    }
}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

/**
 * Author -
 * User: kansal
 * Date: 9/3/11
 * Time: 5:28 PM
 */
public class CF85C {
    public static void main(String[] args) {
        reader = new BufferedReader(new InputStreamReader(System.in));

        int height = nextInt(), width = nextInt();
        if (width > height) {
            int t = width;
            width = height;
            height = t;
        }

        final int INF = height * width + 10;
        final int ALL_BITS = (1 << width)  - 1;
        int[][][] dp = new int[height + 1][1 << width][1 << width];
        for (int[][] ints : dp) {
            for (int[] anInt : ints) {
                Arrays.fill(anInt, INF);
            }
        }

        dp[0][0][0] = 0;
        for(int r = 0; r < height; ++r) {
            for(int uncovered = 0; uncovered < (1 << width); ++uncovered) {
                for(int mask = 0; mask < (1 << width); ++mask) {
                    if (dp[r][uncovered][mask] == INF) {
                        continue;
                    }

                    for(int curMask = uncovered; curMask < (1 << width); curMask = (curMask + 1) | uncovered) {
                        int curCovered = (mask | curMask);
                        curCovered |= (curMask >> 1);
                        curCovered |= (ALL_BITS & (curMask << 1));

                        int curUncovered = ALL_BITS ^ curCovered;
                        dp[r+1][curUncovered][curMask] = Math.min(dp[r+1][curUncovered][curMask], dp[r][uncovered][mask] + Integer.bitCount(curMask));
                    }
                }
            }
        }

        int res = INF;
        for(int x: dp[height][0]) res = Math.min(res, x);

        System.out.println(height * width - res);
    }

    public static BufferedReader reader;

    public static StringTokenizer tokenizer = null;

    static String nextToken() {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            try {
                tokenizer = new StringTokenizer(reader.readLine());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return tokenizer.nextToken();
    }

    static public int nextInt() {
        return Integer.parseInt(nextToken());
    }

    static public long nextLong() {
        return Long.parseLong(nextToken());
    }

    static public String next() {
        return nextToken();
    }

    static public String nextLine() {
        try {
            return reader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}

import java.io.*;
import java.util.*;

import static java.lang.Math.*;

public class Code implements Runnable {

    public static void main(String[] args) throws IOException {
        new Thread(new Code()).start();
    }

    private void solve() throws IOException {
        int n = nextInt(), m = nextInt();
        if(n > m) {
            n ^= m;
            m ^= n;
            n ^= m;
        }

        int[][][] dp = new int[41][64][64];
        for(int i = 0; i < 41; ++i)
            for(int j = 0; j < 64; ++j)
                for(int k = 0; k < 64; ++k) dp[i][j][k] = Integer.MAX_VALUE / 2;

        for(int i = 0; i < 64; ++i) dp[0][0][i] = countBit(i);

        for(int i = 1; i <= m; ++i) {
            for(int cur = 0; cur < 64; ++cur) {
                for(int next = 0; next < 64; ++next) {
                    for(int prev = 0; prev < 64; ++prev) {
                        if(!isBad(prev, cur, next, n)) {
                            dp[i][cur][next] = min(dp[i][cur][next], dp[i - 1][prev][cur] + countBit(next));
                        }
                    }
                }
            }
        }

        int ans = Integer.MAX_VALUE;
        for(int i = 0; i < 64; ++i) ans = min(ans, dp[m][i][0]);

        writer.println(n * m - ans);
    }

    private boolean isBit(int bits, int pos) {
        return pos < 0 ? false : ((bits & (1 << pos)) != 0);
    }

    private boolean isBad(int prev, int cur, int next, int count) {
        for(int i = 0; i < count; ++i)
            if(!(isBit(cur, i - 1) || isBit(cur, i) || isBit(cur, i + 1) || isBit(prev, i) || isBit(next, i)))  return true;
        return false;
    }

    private int countBit(int bits) {
        int ans = 0;
        for(int i = 0; i < 6; ++i)  ans += (bits & (1 << i)) > 0 ? 1 : 0;
        return ans;
    }

    private class Pair<E extends Comparable, V extends Comparable> implements Comparable<Pair<E, V>> {
        public Pair(E first, V second) {
            this.first = first;
            this.second = second;
        }

        @Override
        public int compareTo(Pair<E, V> obj) {
            if(first == obj.first)  return second.compareTo(obj.second);
            return first.compareTo(obj.first);
        }

        @Override
        public boolean equals(Object obj) {
            Pair other = (Pair)obj;
            return first.equals(other.first) && second.equals(other.second);
        }

        public E first;
        public V second;
    }

    @Override
    public void run() {
        try {
            if(in.equals(""))   reader = new BufferedReader(new InputStreamReader(System.in));
            else    reader = new BufferedReader(new FileReader(in));

            if(out.equals(""))  writer = new PrintWriter(new OutputStreamWriter(System.out));
            else    writer = new PrintWriter(new FileWriter(out));
            solve();
        } catch(IOException e) {
            e.printStackTrace();
        } finally {
            try {
                reader.close();
                writer.close();
            } catch(IOException e) {
                e.printStackTrace();
            }
        }
    }

    private int nextInt() throws IOException {
        return Integer.parseInt(nextToken());
    }

    private long nextLong() throws IOException {
        return Long.parseLong(nextToken());
    }

    private double nextDouble() throws IOException {
        return Double.parseDouble(nextToken());
    }

    private float nextFloat() throws IOException {
        return Float.parseFloat(nextToken());
    }

    private String nextToken() throws IOException {
        while(st == null || !st.hasMoreTokens())    st = new StringTokenizer(reader.readLine());
        return st.nextToken();
    }

    private String in = "", out = "";
    private BufferedReader reader;
    private PrintWriter writer;
    private StringTokenizer st;
}

import java.io.InputStreamReader;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import java.math.BigInteger;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author AlexFetisov
 */
public class Main {
	public static void main(String[] args) {
		InputStream inputStream = System.in;
		OutputStream outputStream = System.out;
		InputReader in = new InputReader(inputStream);
		PrintWriter out = new PrintWriter(outputStream);
		TaskC solver = new TaskC();
		solver.solve(1, in, out);
		out.close();
	}
}

class TaskC {
    public void solve(int testNumber, InputReader in, PrintWriter out) {
        n = in.nextInt();
        m = in.nextInt();
        if (n > m) {
            int t = m;
            m = n;
            n = t;
        }
//        if (n == 1) {
//            out.println(m - ((m + 2) / 3));
//        } else if (n == 2) {
//            int cur = 2;
//            for (int i = 2; i <= m; ++i) {
//                if (i % 2 == 0 && i > 2) {
//                    ++cur;
//                }
//            }
//            out.println(n * m - cur);
//        } else {
            f = new int[n][m];
            res = Integer.MAX_VALUE;
            cur = 0;
            step = 1;
            numFree = n * m;
            rec(0, 0);
            out.println(n * m - res);
//        }
    }

    private void rec(int x, int y) {
        if (numFree == 0) {
            res = Math.min(res, cur);
            return;
        }
        if (x >= n) return;
        if (y >= m) {
            rec(x + 1, 0);
            return;
        }
        if (f[x][y] != 0) {
            rec(x, y + 1);
            return;
        }

        put(x, y);
        rec(x, y + 1);
        remove(x, y);

        if (isValid(x + 1, y)) {
            put(x + 1, y);
            rec(x, y + 1);
            remove(x + 1, y);
        }

        if (isValid(x, y + 1)) {
            put(x, y + 1);
            rec(x, y + 1);
            remove(x, y + 1);
        }
    }

    private void put(int x, int y) {
        for (int i = 0; i < 5; ++i) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (isValid(nx, ny)) {
                if (f[nx][ny] == 0) {
                    --numFree;
                    f[nx][ny] = step;
                }
            }
        }
        ++step;
        ++cur;
    }

    private void remove(int x, int y) {
        --step;
        for (int i = 0; i < 5; ++i) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (isValid(nx, ny)) {
                if (f[nx][ny] == step) {
                    ++numFree;
                    f[nx][ny] = 0;
                }
            }
        }
        --cur;
    }

    private boolean isValid(int x, int y) {
        return x >= 0 && y >= 0 && x < n && y < m;
    }

    int n, m;
    int[] dx = new int[] {-1, 0, 1, 0, 0};
    int[] dy = new int[] {0, 1, 0, -1, 0};

    int step;
    int numFree;
    int cur;
    int res;
    int[][] f;
}

class InputReader {
    private BufferedReader reader;
    private StringTokenizer stt;

    public InputReader(InputStream stream) {
        reader = new BufferedReader(new InputStreamReader(stream));
    }

    public String nextLine() {
        try {
            return reader.readLine().trim();
        } catch (IOException e) {
            return null;
        }
    }

    public String nextString() {
        while (stt == null || !stt.hasMoreTokens()) {
            stt = new StringTokenizer(nextLine());
        }
        return stt.nextToken();
    }

    public int nextInt() {
        return Integer.parseInt(nextString());
    }

}

/**
 * @author derrick20
 */
import java.io.*;
import java.util.*;

public class PetyaSpiders implements Runnable {
    public static void main(String[] args) throws Exception {
        new Thread(null, new PetyaSpiders(), ": )", 1 << 28).start();
    }

    public void run() {
        FastScanner sc = new FastScanner();
        PrintWriter out = new PrintWriter(System.out);

        N = sc.nextInt();
        M = sc.nextInt();
        if (N > M) {
            int temp = N;
            N = M;
            M = temp;
        }
        // if the max is 1, then 1x1. Else, one dimension is at least 2, so
        // our idea of sliding forward a 2xN window works
        if (M == 1) {
            out.println(0);
        } else {
            int[][][] dp = new int[M][1 << N][1 << N];
            // N, M are at least 2. N <= 6
            // base case: try all prev and curr masks, let next = 0
            for (int prev = 0; prev < 1 << N; prev++) {
                // we cannot allow the previous to have anything except for 0's.
                // this matters for M = 2, since at the end of the dp we are
                // doing a special case for the final column where we need to
                // ensure both curr AND next are validly covered
                if (prev == 0) {
                    for (int curr = 0; curr < 1 << N; curr++) {
                        // all of these will satisfy the nonexistent columns, of course
                        dp[0][prev][curr] = Integer.bitCount(curr);
                    }
                } else {
                    // not allowed to have nonzero previous spiders
                    Arrays.fill(dp[0][prev], oo);
                }
            }
            for (int prev = 0; prev < 1 << N; prev++) {
                for (int curr = 0; curr < 1 << N; curr++) {
                    if (isValid(0, prev, curr)) {
                        dp[1][prev][curr] = Integer.bitCount(prev) + Integer.bitCount(curr);
                    } else {
                        dp[1][prev][curr] = oo;
                    }
                }
            }
            for (int col = 2; col <= M - 2; col++) {
                for (int next = 0; next < 1 << N; next++) {
                    for (int curr = 0; curr < 1 << N; curr++) {
                        dp[col][curr][next] = oo;
                        for (int prev = 0; prev < 1 << N; prev++) {
                            if (dp[col - 1][prev][curr] != oo && isValid(prev, curr, next)) {
                                dp[col][curr][next] = Math.min(dp[col][curr][next], dp[col - 1][prev][curr] + Integer.bitCount(next));
                            }
                        }
                    }
                }
            }
            // to get the answer, we need to act on the last column by trying to
            // ensure that both the prev row AND the current row are valid
            // basically, isValid(prev, curr, next) && isValid(curr, next, 0)
            int ans = oo;
            for (int next = 0; next < 1 << N; next++) {
                for (int curr = 0; curr < 1 << N; curr++) {
                    dp[M - 1][curr][next] = oo;
                    for (int prev = 0; prev < 1 << N; prev++) {
                        if (dp[M - 2][prev][curr] != oo && isValid(prev, curr, next) && isValid(curr, next, 0)) {
                            dp[M - 1][curr][next] = Math.min(dp[M - 1][curr][next], dp[M - 2][prev][curr] + Integer.bitCount(next));
                            ans = Math.min(ans, dp[M - 1][curr][next]);
                        }
                    }
                }
            }
//            for (int c = 1; c < M; c++) {
//                System.out.println("For col = " + c);
//                for (int prev = 0; prev < 1 << N; prev++) {
//                    System.out.println("Prev = " + Integer.toBinaryString(prev));
//                    for (int curr = 0; curr < 1 << N; curr++) {
//                        System.out.println("Curr = " + Integer.toBinaryString(curr) + " gives " + dp[c][prev][curr] + " ");
//                    }
//                }
//            }
            out.println(N * M - ans);
        }
        out.close();
    }

    static int N, M;
    static int oo = 999;
    static int[] dr = {1, 0, -1, 0}, dc = {0, 1, 0, -1};

    static boolean isValid(int prev, int curr, int next) {
        boolean[][] grid = new boolean[N][3];
        int[] subsets = {prev, curr, next};
        for (int r = 0; r < N; r++) {
            for (int c = 0; c < 3; c++) {
                if ((subsets[c] & 1) > 0) {
                    grid[r][c] = true;
                    for (int k = 0; k < 4; k++) {
                        int r2 = r + dr[k];
                        int c2 = c + dc[k];
                        if (0 <= r2 && r2 <= N - 1 && 0 <= c2 && c2 <= 2) {
                            grid[r2][c2] = true;
                        }
                    }
                }
                subsets[c] >>= 1;
            }
        }
        for (int r = 0; r < N; r++) {
            if (!grid[r][1]) {
                // we only need to ensure the middle is safe
                return false;
            }
        }
        return true;
    }

    static class FastScanner {
        private int BS = 1 << 16;
        private char NC = (char) 0;
        private byte[] buf = new byte[BS];
        private int bId = 0, size = 0;
        private char c = NC;
        private double cnt = 1;
        private BufferedInputStream in;

        public FastScanner() {
            in = new BufferedInputStream(System.in, BS);
        }

        public FastScanner(String s) {
            try {
                in = new BufferedInputStream(new FileInputStream(new File(s)), BS);
            } catch (Exception e) {
                in = new BufferedInputStream(System.in, BS);
            }
        }

        private char getChar() {
            while (bId == size) {
                try {
                    size = in.read(buf);
                } catch (Exception e) {
                    return NC;
                }
                if (size == -1) return NC;
                bId = 0;
            }
            return (char) buf[bId++];
        }

        public int nextInt() {
            return (int) nextLong();
        }

        public int[] nextInts(int N) {
            int[] res = new int[N];
            for (int i = 0; i < N; i++) {
                res[i] = (int) nextLong();
            }
            return res;
        }

        public long[] nextLongs(int N) {
            long[] res = new long[N];
            for (int i = 0; i < N; i++) {
                res[i] = nextLong();
            }
            return res;
        }

        public long nextLong() {
            cnt = 1;
            boolean neg = false;
            if (c == NC) c = getChar();
            for (; (c < '0' || c > '9'); c = getChar()) {
                if (c == '-') neg = true;
            }
            long res = 0;
            for (; c >= '0' && c <= '9'; c = getChar()) {
                res = (res << 3) + (res << 1) + c - '0';
                cnt *= 10;
            }
            return neg ? -res : res;
        }

        public double nextDouble() {
            double cur = nextLong();
            return c != '.' ? cur : cur + nextLong() / cnt;
        }

        public double[] nextDoubles(int N) {
            double[] res = new double[N];
            for (int i = 0; i < N; i++) {
                res[i] = nextDouble();
            }
            return res;
        }

        public String next() {
            StringBuilder res = new StringBuilder();
            while (c <= 32) c = getChar();
            while (c > 32) {
                res.append(c);
                c = getChar();
            }
            return res.toString();
        }

        public String nextLine() {
            StringBuilder res = new StringBuilder();
            while (c <= 32) c = getChar();
            while (c != '\n') {
                res.append(c);
                c = getChar();
            }
            return res.toString();
        }

        public boolean hasNext() {
            if (c > 32) return true;
            while (true) {
                c = getChar();
                if (c == NC) return false;
                else if (c > 32) return true;
            }
        }
    }

    static void ASSERT(boolean assertion, String message) {
        if (!assertion) throw new AssertionError(message);
    }

    static void ASSERT(boolean assertion) {
        if (!assertion) throw new AssertionError();
    }
}

// practice with rainboy
import java.io.*;
import java.util.*;

public class CF111C extends PrintWriter {
	CF111C() { super(System.out, true); }
	Scanner sc = new Scanner(System.in);
	public static void main(String[] $) {
		CF111C o = new CF111C(); o.main(); o.flush();
	}

	int encode(int[] aa, int m) {
		int a = 0;
		for (int j = 0; j < m; j++)
			a = a * 3 + aa[j];
		return a;
	}
	void decode(int[] aa, int m, int a, int base) {
		for (int j = m - 1; j >= 0; j--) {
			aa[j] = a % base;
			a /= base;
		}
	}
	void main() {
		int n = sc.nextInt();
		int m = sc.nextInt();
		if (n < m) {
			int tmp = n; n = m; m = tmp;
		}
		int p = 1;
		for (int j = 0; j < m; j++)
			p *= 3;
		int[] dp = new int[p];
		int[] dq = new int[p];
		int[] aa = new int[m];
		int[] bb = new int[m];
		for (int j = 0; j < m; j++)
			aa[j] = 1;
		Arrays.fill(dp, -1);
		dp[encode(aa, m)] = 0;
		while (n-- > 0) {
			Arrays.fill(dq, -1);
			for (int a = 0; a < p; a++) {
				if (dp[a] < 0)
					continue;
				decode(aa, m, a, 3);
				for (int b = 0; b < 1 << m; b++) {
					decode(bb, m, b, 2);
					boolean bad = false;
					for (int j = 0; j < m; j++)
						if (aa[j] == 0 && bb[j] == 0) {
							bad = true;
							break;
						}
					if (bad)
						continue;
					int cnt = 0;
					for (int j = 0; j < m; j++)
						if (bb[j] == 1) {
							bb[j] = 2;
							cnt++;
						}
					for (int j = 0; j < m; j++)
						if (bb[j] == 0 && (aa[j] == 2 || j > 0 && bb[j - 1] == 2 || j + 1 < m && bb[j + 1] == 2))
							bb[j] = 1;
					int a_ = encode(bb, m);
					dq[a_] = Math.max(dq[a_], dp[a] + m - cnt);
				}
			}
			int[] tmp = dp; dp = dq; dq = tmp;
		}
		int ans = 0;
		for (int a = 0; a < p; a++) {
			if (dp[a] <= ans)
				continue;
			decode(aa, m, a, 3);
			boolean bad = false;
			for (int j = 0; j < m; j++)
				if (aa[j] == 0) {
					bad = true;
					break;
				}
			if (!bad)
				ans = dp[a];
		}
		println(ans);
	}
}

import java.io.*;
import java.util.*;

public class Main
{
	static final boolean _DEBUG = false;

	private static class MyScanner {
		BufferedReader br;
		StringTokenizer st;

		public MyScanner(BufferedReader _br) {
			br = _br;
		}

		String next() {
			while (st == null || !st.hasMoreElements()) {
				try {
					st = new StringTokenizer(br.readLine());
				} catch (Exception e) {
					e.printStackTrace();
					return "";
				}
			}
			return st.nextToken();
		}

		int nextInt() {
			return Integer.parseInt(next());
		}

		long nextLong() {
			return Long.parseLong(next());
		}

		double nextDouble() {
			return Double.parseDouble(next());
		}

		String nextLine() {
			String str = "";
			try {
				str = br.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return str;
		}

	}

	static MyScanner   scan;
	static PrintWriter out;

	static int debugCount = 0;
	static void debug(String msg) {
		if (_DEBUG && debugCount < 200) {
			out.println(msg);
			out.flush();
			debugCount++;
		}
	}

    public static void main (String args[]) throws IOException {
//    	scan = new MyScanner(new BufferedReader(new FileReader("test.in")));
    	scan = new MyScanner(new BufferedReader(new InputStreamReader(System.in)));
    	out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
        Main inst = new Main();
        inst.execute();
        out.close();
    }

    int N, M, pow;
    int[] counts;
    int[][][] dp;

    void execute() {
    	N = scan.nextInt();
    	M = scan.nextInt();
    	if (N < M) {
    		int temp = N;
    		N = M;
    		M = temp;
    	}
    	pow = 1<<M;
    	counts = new int[pow];
    	dp = new int[N][pow][pow];
    	for (int[][] i : dp) {
    		for (int[] j : i) {
    			Arrays.fill(j, -1);
    		}
    	}
    	for (int i = 0; i < pow; i++) {
    		counts[i] = Integer.bitCount(i);
        	dp[0][i][0] = counts[i];
    	}
    	int spiders = Integer.MAX_VALUE;
    	for (int y = 0; y < N-1; y++) {
    		for (int i = 0; i < pow; i++) {
    			for (int j = 0; j < pow; j++) {
    				if (dp[y][i][j] == -1) {
    					continue;
    				}
    				for (int k = 0; k < pow; k++) {
    					if (((i|(i<<1)|(i>>1)|j|k)&(pow-1))==(pow-1)) {
    						int value = dp[y][i][j] + counts[k];
    						if (dp[y+1][k][i] == -1 || dp[y+1][k][i] > value) {
    							dp[y+1][k][i] = value;
    						}
    					}
    				}
    			}
    		}
    	}
//    	for (int i = 0; i < N; i++) {
//        	System.out.println(Arrays.deepToString(dp[i]));
//    	}
    	for (int i = 0; i < pow; i++) {
    		for (int j = 0; j < pow; j++) {
    			if (dp[N-1][i][j] != -1 && ((i|(i<<1)|(i>>1)|j)&(pow-1))==(pow-1)) {
    				spiders = Math.min(spiders, dp[N-1][i][j]);
//    				System.out.println(spiders+" "+i+" "+j);
    			}
    		}
    	}
    	out.println((N*M)-spiders);
    }
}

/*************************************************************************
    > File Name: cf111c.java
    > Author: Yuchen Wang
    > Mail: wyc8094@gmail.com
    > Created Time: Sat Feb 11 16:51:01 2017
 ************************************************************************/

import java.util.*;
import java.io.*;

public class cf111c
{
	public static int n,m,maxm;
	public static int[][][] dp;
	public static int[] s;

	public static int cal(int cur)
	{
		int res = 0;
		while(cur>0){
			res ++;
			cur = cur&(cur-1);
		}
		return res;
	}

	public static boolean check(int a,int b,int c)
	{
		int res = (maxm-1) & (b | (b<<1) | (b>>1) | a | c);
		if(res == maxm-1)return true;
		else return false;
	}

	public static void main(String[] argv)
	{
		Scanner in = new Scanner(System.in);

		n = in.nextInt();
		m = in.nextInt();
		if(n<m){
			int t = m;
			m = n;
			n = t;
		}

		maxm = 1<<m;
		int i,j,k,l;
		dp = new int[n+1][1<<m][1<<m];
		s = new int[1<<m];

		for(i=0;i<n+1;i++){
			for(j=0;j<maxm;j++){
				Arrays.fill(dp[i][j],100);
			}
		}

		for(i=0;i<maxm;i++){
			s[i] = cal(i);
			dp[0][0][i] = 0;
		}

		for(i=1;i<=n;i++){
			for(j=0;j<maxm;j++){
				for(k=0;k<maxm;k++){
					for(l=0;l<maxm;l++){
						if(dp[i-1][k][l]!=100 && check(k,l,j)){
							dp[i][l][j] = Math.min(dp[i-1][k][l]+s[l],dp[i][l][j]);
						}
					}
				}
			}
		}

		int ans = 100;
		for(i=0;i<maxm;i++)
			ans = Math.min(dp[n][i][0],ans);

		System.out.println(n*m-ans);
		return;
	}
}

import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.BufferedWriter;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.io.IOException;
import java.util.InputMismatchException;
import java.io.PrintWriter;
import java.io.OutputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author Alex
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        InputReader in = new InputReader(inputStream);
        OutputWriter out = new OutputWriter(outputStream);
        TaskC solver = new TaskC();
        solver.solve(1, in, out);
        out.close();
    }
    static class TaskC {
        int BAD = 11111;
        int rows;
        int cols;
        HashMap<IntIntPair, Integer>[] mem;

        boolean contains(int mem, int index) {
            if(index < 0) return false;
            return ((mem >> index) & 1) == 1;
        }

        int add(int mem, int index) {
            if(((mem >> index) & 1) == 0) {
                mem += (1 << index);
            }
            return mem;
        }

        int size(int mem) {
            int res = 0;
            while(mem > 0) {
                if(mem % 2 == 1) res++;
                mem /= 2;
            }
            return res;
        }

        void test() {
            if(contains(5, 0) == false) throw new RuntimeException();
            if(contains(5, 1) == true) throw new RuntimeException();
            if(contains(5, -1) == true) throw new RuntimeException();
            if(contains(5, 2) == false) throw new RuntimeException();
            if(contains(5, 3) == true) throw new RuntimeException();
            if(add(0, 2) != 4) throw new RuntimeException();
            if(add(4, 0) != 5) throw new RuntimeException();
            if(add(5, 0) != 5) throw new RuntimeException();
            if(size(5) != 2) throw new RuntimeException();
        }

        int dp(int row, int remabove, int squareabove) {
            if(row == rows) {
                if(remabove == 0) return 0;
                return BAD;
            }
            if(mem[row].containsKey(new IntIntPair(remabove, squareabove)))
                return mem[row].get(new IntIntPair(remabove, squareabove));
            int res = BAD;
            int possibilities = 1 << cols;
            for(int poss = 0; poss < possibilities; poss++) {
                int have = 0;
                for(int j = 0; j < cols; j++)
                    if(((poss >> j) & 1) == 1) {
                        have += 1 << j;
                    }
                boolean works = true;
                for(int above = 0; above < cols; above++)
                    if(((remabove >> above) & 1) == 1) {
                        if(((have >> above) & 1) == 0) {
                            works = false;
                            break;
                        }
                    }
                if(works) {
                    int remhere = 0;
                    for(int j = 0; j < cols; j++) {
                        if(!contains(have, j - 1) && !contains(have, j) && !contains(have, j + 1) && !contains(squareabove, j)) {
                            remhere = add(remhere, j);
                        }
                    }
                    res = Math.min(res, size(have) + dp(row + 1, remhere, have));
                }
            }
            mem[row].put(new IntIntPair(remabove, squareabove), res);
            return res;
        }

        public void solve(int testNumber, InputReader in, OutputWriter out) {
            test();
            int n = in.readInt(), m = in.readInt();
            cols = Math.min(n, m);
            rows = Math.max(n, m);
            mem = new HashMap[rows];
            for(int i = 0; i < mem.length; i++) mem[i] = new HashMap<>();
            int res = dp(0, 0, 0);
            out.printLine(cols * rows - res);
        }

    }

    static class IntIntPair implements Comparable<IntIntPair> {
        public final int first;
        public final int second;

        public IntIntPair(int first, int second) {
            this.first = first;
            this.second = second;
        }

        public boolean equals(Object o) {
            if(this == o) return true;
            if(o == null || getClass() != o.getClass()) return false;

            IntIntPair pair = (IntIntPair) o;

            return first == pair.first && second == pair.second;

        }

        public int hashCode() {
            int result = Integer.hashCode(first);
            result = 31 * result + Integer.hashCode(second);
            return result;
        }

        public String toString() {
            return "(" + first + "," + second + ")";
        }

        @SuppressWarnings({"unchecked"})
        public int compareTo(IntIntPair o) {
            int value = Integer.compare(first, o.first);
            if(value != 0) {
                return value;
            }
            return Integer.compare(second, o.second);
        }

    }

    static class InputReader {
        private InputStream stream;
        private byte[] buf = new byte[1024];
        private int curChar;
        private int numChars;
        private SpaceCharFilter filter;

        public InputReader(InputStream stream) {
            this.stream = stream;
        }

        public int read() {
            if(numChars == -1)
                throw new InputMismatchException();
            if(curChar >= numChars) {
                curChar = 0;
                try {
                    numChars = stream.read(buf);
                } catch(IOException e) {
                    throw new InputMismatchException();
                }
                if(numChars <= 0)
                    return -1;
            }
            return buf[curChar++];
        }

        public int readInt() {
            int c = read();
            while(isSpaceChar(c))
                c = read();
            int sgn = 1;
            if(c == '-') {
                sgn = -1;
                c = read();
            }
            int res = 0;
            do {
                if(c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = read();
            } while(!isSpaceChar(c));
            return res * sgn;
        }

        public boolean isSpaceChar(int c) {
            if(filter != null)
                return filter.isSpaceChar(c);
            return isWhitespace(c);
        }

        public static boolean isWhitespace(int c) {
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        public interface SpaceCharFilter {
            public boolean isSpaceChar(int ch);

        }

    }

    static class OutputWriter {
        private final PrintWriter writer;

        public OutputWriter(OutputStream outputStream) {
            writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
        }

        public OutputWriter(Writer writer) {
            this.writer = new PrintWriter(writer);
        }

        public void close() {
            writer.close();
        }

        public void printLine(int i) {
            writer.println(i);
        }

    }
}

import java.util.*;

public class cf112e {
    static int n,m,s;
    static int[][][] memo;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        m = in.nextInt();
        if(n > m) {
            int tmp = n;
            n = m;
            m = tmp;
        }
        s = (1<<n);
        memo = new int[s][s][m];
        for(int i=0; i<s; i++)
            for(int j=0; j<s; j++)
                Arrays.fill(memo[i][j], -1);
        int ret = go(0,0,0);
        System.out.println(n*m - ret);
    }
    static int go(int last, int trans, int r) {
        if(r==m) {
            if(trans == 0) return 0;
            return 100;
        }
        if(memo[last][trans][r] != -1) return memo[last][trans][r];
        int best = 100;
        for(int crnt = 0; crnt < s; crnt++) {
            if((trans & ~crnt) != 0) continue; //certain ones have to be here
            for(int pass = 0; pass < s; pass++) {
                int tmp = ((1<<n)-1) & ~last;   //move back the ones you can
                if((pass & ~tmp) != 0) continue; //certain ones have to move on
                tmp = tmp & ~pass; //find which ones stay
                boolean fail = false;
                for(int k=0; k<n; k++) //make sure that the ones that stay
                    if(isSet(tmp,k) && !(isSet(crnt,k-1) || isSet(crnt,k) || isSet(crnt,k+1)))
                        fail = true;
                if(fail) continue;
                best = Math.min(best, Integer.bitCount(crnt) + go(crnt,pass,r+1));
            }
        }
        return memo[last][trans][r] = best;
    }
    static boolean isSet(int x, int p) {
        if(p < 0 || p >= n) return false;
        return (x & (1<<p)) != 0;
    }
}

import java.lang.*;
import java.math.BigInteger;
import java.io.*;
import java.util.*;

public class Solution implements Runnable{
    public static BufferedReader br;
    public static PrintWriter out;
    public static StringTokenizer stk;
    public static boolean isStream = true;

    public static void main(String[] args) throws IOException {
    	if (isStream) {
            br = new BufferedReader(new InputStreamReader(System.in));
        } else {
            br = new BufferedReader(new FileReader("in.txt"));
        }
        out = new PrintWriter(System.out);
        new Thread(new Solution()).start();
    }

    public void loadLine() {
        try {
            stk = new StringTokenizer(br.readLine());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String nextLine() {
        try {
            return br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }

    public String nextWord() {
        while (stk==null||!stk.hasMoreTokens()) loadLine();
        return stk.nextToken();
    }

    public Integer nextInt() {
        while (stk==null||!stk.hasMoreTokens()) loadLine();
        return Integer.valueOf(stk.nextToken());
    }

    public Long nextLong() {
        while (stk==null||!stk.hasMoreTokens()) loadLine();
        return Long.valueOf(stk.nextToken());
    }

    public Double nextDouble() {
        while (stk==null||!stk.hasMoreTokens()) loadLine();
        return Double.valueOf(stk.nextToken());
    }

    public Float nextFloat() {
        while (stk==null||!stk.hasMoreTokens()) loadLine();
        return Float.valueOf(stk.nextToken());
    }

    public void run() {
    	int n = nextInt();
    	int m = nextInt();
    	if (n > m) {
    		int sw = n;
    		n = m;
    		m = sw;
    	}
    	int[][] A = new int[1<<n][1<<n];
    	for (int m1 = 0; m1 < 1<<n; ++m1) { // Who goes out
    		for (int m2 = 0; m2 < 1<<n; ++m2) {  // Who comes
    			int[] arr = new int[n]; // Who remains
    			for (int i = 0; i < n; ++i) {
    				arr[i] = (~(m1>>i))&1;
    			}
    			int[] m2a = new int[n];
    			for (int i = 0; i < n; ++i) {
    				m2a[i] = (m2>>i)&1;
    			}
    			int cnt = 0;
    			for (int i = 0; i < n; ++i) {
    				if (arr[i] == 1) {
    					if (i > 0 && m2a[i-1] == 1) {
    						continue;
    					}
    					if (i < n-1 && m2a[i+1] == 1) {
    						continue;
    					}
    					if (m2a[i] == 1) {
    						continue;
    					}
    					if (i < n-1) {
    						m2a[i+1] = 1;
    					} else {
    						m2a[i] = 1;
    					}
    				}
    			}
    			for (int i = 0; i < n; ++i) {
    				if (m2a[i] == 1) {
    					cnt++;
    				}
    			}
    			A[m1][m2] = cnt;
    		}
    	}
    	int MAX = 10000;
    	int[][][] dp = new int[m+1][1<<n][1<<n];
    	for (int i = 0; i < m+1; i++) {
    		for (int m1 = 0; m1 < 1<<n; ++m1) {
    			Arrays.fill(dp[i][m1], MAX);
    		}
    	}

		dp[0][0][0] = 0;

    	for (int i = 0; i < m; i++) {
    		for (int m1 = 0; m1 < 1<<n; ++m1) {
    			for (int m2 = 0; m2 < 1<<n; ++m2) {
    				if (dp[i][m1][m2] != MAX) {
	    				for (int nm1 = 0; nm1 < 1<<n; ++nm1) {
	    	    			for (int nm2 = 0; nm2 < 1<<n; ++nm2) {
	    	        			if ((m1 & nm1) == 0) {
	    	        				int sm1 = m1|nm1;
	    	        				int sm2 = m2|nm2;
	    	        				int cnt = A[sm1][sm2];
	    	        				dp[i+1][nm2][nm1] = Math.min(dp[i+1][nm2][nm1], dp[i][m1][m2]+cnt);
	    	        			}
	    	        		}
	    	    		}
    				}
        		}
    		}
    	}

    	out.println(n*m-dp[m][0][0]);
    	out.flush();
    }
}

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.Map.Entry;

public class Solution implements Runnable {

	public static void main(String[] args) {
		(new Thread(new Solution())).start();
	}

	BufferedReader in;
	PrintWriter out;
	StringTokenizer st;

	String nextToken() throws IOException {
		while (st == null || !st.hasMoreTokens()) {
			String r = in.readLine();
			if (r == null) return null;
			st = new StringTokenizer(r);
		}
		return st.nextToken();
	}

	int nextInt() throws IOException {
		return Integer.parseInt(nextToken());
	}

	long nextLong() throws IOException {
		return Long.parseLong(nextToken());
	}

	double nextDouble() throws IOException {
		return Double.parseDouble(nextToken());
	}

	int count(int q, int n) {
		int ans = 0;
		for (int i = 0; i < n; i++) {
			if (q % 3 != 1) ans++;
			q /= 3;
		}
		return ans;
	}

	int count2(int q, int n) {
		int ans = 0;
		for (int i = 0; i < n; i++) {
			if (q % 3 == 2) ans++;
			q /= 3;
		}
		return ans;
	}

	int sz;

	int[] decode(int q, int n) {
		int[] res = new int[n];
		for (int i = 0; i < n; i++) {
			res[i] = q % 3;
			q /= 3;
		}
		return res;
	}

	boolean[][] precalc(int n, int m) {
		boolean[][] res = new boolean[sz][sz];
		for (int i = 0; i < sz; i++) {
			int[] ai = decode(i, n);
			for (int j = 0; j < sz; j++) {
				int[] aj = decode(j, n);
				boolean f = true;
				for (int k = 0; k < n && f; k++) {
					if (aj[k] == 0) {
						f = false;
						if (k > 0 && aj[k - 1] == 1) f = true;
						if (k < n - 1 && aj[k + 1] == 1) f = true;
						if (ai[k] == 1) f = true;
					}
					if (f && ai[k] == 2) f = (aj[k] == 1);
				}
				res[i][j] = f;
			}
		}
		return res;
	}

	void solve() throws Exception {
		int n = nextInt();
		int m = nextInt();
		if (n > m) { int t = n; n = m; m = t; }
		sz = 1;
		for (int i = 0; i < n; i++) sz *= 3;
		boolean[][] a = precalc(n, m);
		int[][] d = new int[m + 1][sz];
		Arrays.fill(d[0], -1);
		d[0][0] = 0;
		for (int i = 1; i <= m; i++) {
			Arrays.fill(d[i], -1);
			for (int j = 0; j < sz; j++) {
				if (d[i - 1][j] != -1) {
					for (int k = 0; k < sz; k++) {
						if (a[j][k]) {
							d[i][k] = Math.max(d[i][k], d[i - 1][j] + count(k, n));
						}
					}
				}
			}
		}
		int ans = 0;
		for (int i = 0; i < sz; i++) if (count2(i, n) == 0) ans = Math.max(ans, d[m][i]);
		out.println(ans);
	}

	public void run() {
		Locale.setDefault(Locale.UK);
		try {
			in = new BufferedReader(new InputStreamReader(System.in));
			out = new PrintWriter(System.out);
//			in = new BufferedReader(new FileReader("knights.in"));
//			out = new PrintWriter("knights.out");
			solve();
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
		} finally {
			out.flush();
		}
	}

}

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    static int[][][] dp;
    public static void main(String[] args) {
        Scanner r = new Scanner(System.in);

        int n = r.nextInt();
        int m = r.nextInt();

        if(n > m){int t = n; n = m; m = t;}

        dp = new int[m+1][1 << 7][1 << 7];
        for(int[][] i : dp)
            for(int[] j : i)
                Arrays.fill(j, -1);
        int min = go(m, 0, (1<<n) -1, n, m);

        System.out.println(n * m - min);
    }
    private static int go(int rem, int prev, int need, int n, int m) {
//      for(int i = rem; i < m; i++)
//          System.out.print(" ");
//      System.out.println(rem + ": " + prev +","+need);
        if(rem == 0)return prev == 0?0:1 << 20;
        if(dp[rem][prev][need] != -1)return dp[rem][prev][need];

        int min = 1 << 20;
        for(int now = 0; now < 1 << n; now++){
            if((~now & prev) != 0)continue;

            int after = need & ~(now) & ~(now << 1) & ~(now >> 1);
            int next = ~(now) & ((1 << n)-1);
            int current = Integer.bitCount(now) + go(rem-1, after ,next, n, m);
            min = Math.min(min, current);
        }
        return dp[rem][prev][need] = min;
    }
}

import static java.lang.Math.*;
import static java.lang.System.currentTimeMillis;
import static java.lang.System.exit;
import static java.lang.System.arraycopy;
import static java.util.Arrays.sort;
import static java.util.Arrays.binarySearch;
import static java.util.Arrays.fill;
import java.util.*;
import java.io.*;

public class Main {

	public static void main(String[] args) throws IOException {
		try {
			if (new File("input.txt").exists())
				System.setIn(new FileInputStream("input.txt"));
		} catch (SecurityException e) {
		}
		new Thread(null, new Runnable() {
			public void run() {
				try {
					new Main().run();
				} catch (Throwable e) {
					e.printStackTrace();
					exit(999);
				}
			}
		}, "1", 1 << 23).start();
	}

	BufferedReader in;
	PrintWriter out;
	StringTokenizer st = new StringTokenizer("");

	int n, m;
	int dp[][][];
	int MV = Integer.MAX_VALUE >> 1;
	int ans = MV;

	private void run() throws IOException {
		in = new BufferedReader(new InputStreamReader(System.in));
		out = new PrintWriter(System.out);

		n = nextInt();
		m = nextInt();
		if(n < m){ int d = n; n = m; m = d; }
		int M = 1 << m;
		dp = new int[n][M][M];
		for(int a[][] : dp)
			for(int b[] : a)
				fill(b, MV);

//		for(int i = 0 ; i < M; i++){
//			int next = (( i | (i << 1) | (i >> 1) ) & (M - 1));
//			if(  next!= (M -1))
//				continue;
//			if(n > 1)
//				dp[0][next][0] = Integer.bitCount(i);
//			else
//				ans = min(ans, Integer.bitCount(i));
//		}
		dp[0][0][0] = 0;

		for(int i = 0; i < n; i++)
			for(int m1 = 0; m1 < M; m1++)
				for(int m2 = 0; m2 < M; m2++){
					if(dp[i][m1][m2] == MV)
						continue;
					for(int nm1 = 0; nm1 < M; nm1++)
						for(int nm2 = 0; nm2 < M; nm2++){
							int res1 = m1 | (nm1) | (nm1 << 1) | (nm1 >> 1) | (nm2);
							res1 &= (M - 1);

							if(res1 != (M - 1))
								continue;

							int res2 = m2 | (nm1) | (nm2 << 1) | (nm2 >> 1) | (nm2);
							res2 &= (M - 1);

//							if(i == 2 && m1 == 3 && m2 == 2 && nm1 == 0 && nm2 == 0){
//								System.err.println("kek");
//								System.err.println(dp[i][m1][m2]);
//								System.err.println(res1 + " | " + res2);
//								System.err.println(M);
//							}

							int next1 = res2 & (M - 1);
							int next2 = nm2 & ( M - 1);
							int over = Integer.bitCount(nm1) + Integer.bitCount(nm2);

							if(i < n - 1)
								dp[i+1][next1][next2] = min(dp[i + 1][next1][next2], dp[i][m1][m2] + over);
							else
								if((res1 & (M - 1)) == (M - 1)){
									ans = min(dp[i][m1][m2] + over, ans);
								}
						}

				}
//		System.err.println(ans);
		out.println(n * m - ans);

		in.close();
		out.close();
	}
	String nextToken() throws IOException {
		while (!st.hasMoreTokens())
			st = new StringTokenizer(in.readLine());
		return st.nextToken();
	}
	int nextInt() throws IOException {
		return Integer.parseInt(nextToken());
	}
	long nextLong() throws IOException {
		return Long.parseLong(nextToken());
	}
	double nextDouble() throws IOException {
		return Double.parseDouble(nextToken());
	}
	String nextLine() throws IOException {
		st = new StringTokenizer("");
		return in.readLine();
	}
	boolean EOF() throws IOException {
		while (!st.hasMoreTokens()) {
			String s = in.readLine();
			if (s == null)
				return true;
			st = new StringTokenizer(s);
		}
		return false;
	}
}

import static java.util.Arrays.*;
import static java.lang.Math.*;
import static java.math.BigInteger.*;
import java.util.*;
import java.math.*;
import java.io.*;

public class C implements Runnable
{
    String file = "input";

    boolean TEST = false;

    void solve() throws IOException
    {
        rows = nextInt();
        cols = nextInt();
        if(cols > rows)
        {
            int t = rows; rows = cols; cols = t;
        }
        dp = new int[rows][cols][1 << cols][1 << cols][1 << cols];
        for(int i = 0; i < rows; i++)
            for(int j = 0; j < cols; j++)
                for(int k = 0; k < 1 << cols; k++)
                    for(int u = 0; u < 1 << cols; u++)
                        for(int v = 0; v < 1 << cols; v++) dp[i][j][k][u][v] = -1;
        out.println(go(0, 0, 0, 0, 0));
    }
    int rows, cols;
    int[][][][][] dp;
    int INF = 1 << 20;
    int go(int i, int j, int a, int b, int c)
    {
        if(i == rows)
        {
            return a == 0 && b == 0 && c == 0 ? 0 : -INF;
        }
        if(i + 1 == rows && b != 0 && c != 0) return -INF;
        if(i + 2 == rows && c != 0) return -INF;
        if(j == cols)
        {
            return go(i + 1, 0, b, c, 0);
        }

        if(dp[i][j][a][b][c] != -1) return dp[i][j][a][b][c];

        if(!test(a, j, -1, -1)) return go(i, j + 1, a, b, c);

        int res = -INF;

        //1
        if(test(a, j, -1, -1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), b, c));
        //2
        if(test(a, j, -1, -1) && test(b, j, -1, -1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), set(b, j, -1, -1), c) + 1);
        //3
        if(j + 2 <= cols && test(a, j, j + 1, -1))
            res = max(res, go(i, j + 2, set(a, j, j + 1, -1), b, c) + 1);
        //4
        if(j + 3 <= cols && test(a, j, j + 1, j + 2))
            res = max(res, go(i, j + 2, set(a, j, j + 1, j + 2), b, c) + 2);
        //5
        if(test(a, j, -1, -1) && test(b, j, -1, -1) && test(c, j, -1, -1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), set(b, j, -1, -1), set(c, j, -1, -1)) + 2);
        //6
        if(j - 1 >= 0 && test(a, j, -1, -1) && test(b, j - 1, j, -1) && test(c, j, -1, -1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), set(b, j - 1, j, -1), set(c, j, -1, -1)) + 3);
        //7
        if(j + 2 <= cols && test(a, j, -1, -1) && test(b, j, j + 1, -1) && test(c, j, -1, -1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), set(b, j, j + 1, -1), set(c, j, -1, -1)) + 3);
        //8
        if(j + 3 <= cols && test(a, j, j + 1, j + 2) && test(b, j + 1, -1, -1))
            res = max(res, go(i, j + 3, set(a, j, j + 1, j + 2), set(b, j + 1, -1, -1), c) + 3);
        //9
        if(j + 2 <= cols && j - 1 >= 0 && test(b, j - 1, j, j + 1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), set(b, j - 1, j, j + 1), c) + 3);
        //10
        if(j + 2 <= cols && j - 1 >= 0 && test(b, j - 1, j, j + 1) && test(c, j, -1, -1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), set(b, j - 1, j, j + 1), set(c, j, -1, -1)) + 4);

        //11
        if(j + 2 <= cols && test(b, j, j + 1, -1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), set(b, j, j + 1, -1), c) + 2);

        //12
        if(j - 1 >= 0 && test(b, j - 1, j, -1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), set(b, j - 1, j, -1), c) + 2);

        //13
        if(j + 2 <= cols && test(a, j, j + 1, -1) && test(b, j, -1, -1))
            res = max(res, go(i, j + 2, set(a, j, j + 1, -1), set(b, j, -1, -1), c) + 2);

        //14
        if(j + 2 <= cols && test(a, j, j + 1, -1) && test(b, j + 1, -1, -1))
            res = max(res, go(i, j + 2, set(a, j, j + 1, -1), set(b, j + 1, -1, -1), c) + 2);

        return dp[i][j][a][b][c] = res;
    }
    int set(int a, int i, int j, int k)
    {
        if(i != -1) a |= 1 << i;
        if(j != -1) a |= 1 << j;
        if(k != -1) a |= 1 << k;
        return a;
    }
    boolean test(int a, int i, int j, int k)
    {
        if(i != -1 && (a >> i & 1) != 0) return false;
        if(j != -1 && (a >> j & 1) != 0) return false;
        if(k != -1 && (a >> k & 1) != 0) return false;
        return true;
    }

    String next() throws IOException
    {
        while(st == null || !st.hasMoreTokens()) st = new StringTokenizer(input.readLine());
        return st.nextToken();
    }

    int nextInt() throws IOException
    {
        return Integer.parseInt(next());
    }

    long nextLong() throws IOException
    {
        return Long.parseLong(next());
    }

    double nextDouble() throws IOException
    {
        return Double.parseDouble(next());
    }

    void print(Object... o)
    {
        System.out.println(deepToString(o));
    }

    void gcj(Object o)
    {
        String s = String.valueOf(o);
        out.println("Case #" + test + ": " + s);
        System.out.println("Case #" + test + ": " + s);
    }

    BufferedReader input;
    PrintWriter out;
    StringTokenizer st;
    int test;

    void init() throws IOException
    {
        if(TEST) input = new BufferedReader(new FileReader(file + ".in"));
        else input = new BufferedReader(new InputStreamReader(System.in));
        out = new PrintWriter(new BufferedOutputStream(System.out));
    }

    public static void main(String[] args) throws IOException
    {
        new Thread(null, new C(), "", 1 << 20).start();
    }

    public void run()
    {
        try
        {
            init();
            if(TEST)
            {
                int runs = nextInt();
                for(int i = 0; i < runs; i++) solve();
            }
            else solve();
            out.close();
        }
        catch(Exception e)
        {
            e.printStackTrace();
            System.exit(1);
        }
    }
}

import java.util.ArrayList;
import java.util.List;
import java.util.InputMismatchException;
import java.math.BigInteger;
import java.util.Iterator;
import java.io.*;
import java.util.Comparator;
import java.util.Arrays;
import java.util.Collection;

/**
 * Generated by Contest helper plug-in
 * Actual solution is at the bottom
 */
public class Main {
	public static void main(String[] args) {
		InputReader in = new StreamInputReader(System.in);
		PrintWriter out = new PrintWriter(System.out);
		run(in, out);
	}

	public static void run(InputReader in, PrintWriter out) {
		Solver solver = new TaskC();
		solver.solve(1, in, out);
		Exit.exit(in, out);
	}
}

abstract class InputReader {
	private boolean finished = false;

	public abstract int read();

	public int readInt() {
		int c = read();
		while (isSpaceChar(c))
			c = read();
		int sgn = 1;
		if (c == '-') {
			sgn = -1;
			c = read();
		}
		int res = 0;
		do {
			if (c < '0' || c > '9')
				throw new InputMismatchException();
			res *= 10;
			res += c - '0';
			c = read();
		} while (!isSpaceChar(c));
		return res * sgn;
	}

	public String readString() {
		int c = read();
		while (isSpaceChar(c))
			c = read();
		StringBuffer res = new StringBuffer();
		do {
			res.appendCodePoint(c);
			c = read();
		} while (!isSpaceChar(c));
		return res.toString();
	}

	private boolean isSpaceChar(int c) {
		return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
	}

	public void setFinished(boolean finished) {
		this.finished = finished;
	}

	public abstract void close();
}

class StreamInputReader extends InputReader {
	private InputStream stream;
	private byte[] buf = new byte[1024];
	private int curChar, numChars;

	public StreamInputReader(InputStream stream) {
		this.stream = stream;
	}

	public int read() {
		if (numChars == -1)
			throw new InputMismatchException();
		if (curChar >= numChars) {
			curChar = 0;
			try {
				numChars = stream.read(buf);
			} catch (IOException e) {
				throw new InputMismatchException();
			}
			if (numChars <= 0)
				return -1;
		}
		return buf[curChar++];
	}

	public void close() {
		try {
			stream.close();
		} catch (IOException ignored) {
		}
	}
}

class Exit {
	private Exit() {
	}

	public static void exit(InputReader in, PrintWriter out) {
		in.setFinished(true);
		in.close();
		out.close();
	}
}

interface Solver {
	public void solve(int testNumber, InputReader in, PrintWriter out);
}

class ArrayUtils {

	public static void fill(int[][] array, int value) {
		for (int[] row : array)
			Arrays.fill(row, value);
	}

	public static void fill(int[][][] array, int value) {
		for (int[][] subArray : array)
			fill(subArray, value);
	}

	}

class TaskC implements Solver {
	public void solve(int testNumber, InputReader in, PrintWriter out) {
		int rowCount = in.readInt();
		int columnCount = in.readInt();
		out.println(rowCount * columnCount - go(Math.min(rowCount, columnCount), Math.max(rowCount, columnCount)));
	}

	private int go(int rowCount, int columnCount) {
		int[][][] result = new int[columnCount][rowCount][1 << (2 * rowCount)];
		ArrayUtils.fill(result, -1);
		return go(0, 0, (1 << rowCount) - 1, result);
	}

	private int go(int column, int row, int mask, int[][][] result) {
		if (column == result.length)
			return (mask == 0 ? 0 : Integer.MAX_VALUE / 2);
		int length = result[column].length;
		if (row == length)
			return go(column + 1, 0, mask, result);
		if (result[column][row][mask] != -1)
			return result[column][row][mask];
		result[column][row][mask] = Integer.MAX_VALUE / 2;
		if ((mask >> (2 * length - 1) & 1) == 0)
			result[column][row][mask] = go(column, row + 1, mask * 2 + (column == result.length - 1 ? 0 : 1), result);
		int newMask = mask;
		newMask &= ~(1 << (length - 1));
		if (row != 0)
			newMask &= ~(1 << length);
		if (row != length - 1)
			newMask &= ~(1 << (length - 2));
		newMask *= 2;
		newMask &= (1 << (2 * length)) - 1;
		return result[column][row][mask] = Math.min(result[column][row][mask], 1 + go(column, row + 1, newMask, result));
	}
}

import java.io.*;

import java.awt.geom.Point2D;
import java.text.*;
import java.math.*;
import java.util.*;

public class Main implements Runnable {

	final String filename = "";

	public void solve() throws Exception {
		int n = iread(), m = iread();
		int INF = -1;
		if (m > n) {
			int t = m;
			m = n;
			n = t;
		}
		int[][][] d = new int[2][1 << m][1 << m];
		for (int i = 0; i < 1 << m; i++)
			Arrays.fill(d[0][i], INF);

		int[] cnt = new int[1 << m];
		for (int i = 0; i < 1 << m; i++)
			cnt[i] = cnt[i / 2] + i % 2;

		int step = 0;
		d[0][0][0] = 0;
		for (int u = 0; u < n; u++) {
			for (int i = 0; i < 1 << m; i++)
				Arrays.fill(d[step ^ 1][i], INF);
			for (int mask1 = 0; mask1 < 1 << m; mask1++)
				for (int mask2 = 0; mask2 < 1 << m; mask2++) {
					int t = d[step][mask1][mask2];
					if (t == INF)
						continue;
					for (int mask = 0; mask < 1 << m; mask++) {
						if ((mask1 & mask) != mask1)
							continue;
						int mask01 = ((1 << m) - 1) & ~mask2;

						for (int j = 0; j < m; j++)
							if ((mask & (1 << j)) != 0) {
								if (j > 0)
									mask01 &= ~(1 << (j - 1));
								mask01 &= ~(1 << j);
								if (j + 1 < m)
									mask01 &= ~(1 << (j + 1));
							}
						int mask02 = mask;

						int t2 = t + cnt[((1 << m) - 1) & ~mask];
						if (d[step ^ 1][mask01][mask02] < t2) {
							d[step ^ 1][mask01][mask02] = t2;
						}
					}
				}
			step ^= 1;
		}
		int ans = INF;
		for (int mask = 0; mask < 1 << m; mask++) {
			ans = Math.max(ans, d[step][0][mask]);
		}
		out.write(ans + "\n");
	}

	public void run() {
		try {
			in = new BufferedReader(new InputStreamReader(System.in));
			out = new BufferedWriter(new OutputStreamWriter(System.out));
			// in = new BufferedReader(new FileReader(filename+".in"));
			// out = new BufferedWriter(new FileWriter(filename+".out"));
			solve();
			out.flush();
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
		}
	}

	public int iread() throws Exception {
		return Integer.parseInt(readword());
	}

	public double dread() throws Exception {
		return Double.parseDouble(readword());
	}

	public long lread() throws Exception {
		return Long.parseLong(readword());
	}

	BufferedReader in;

	BufferedWriter out;

	public String readword() throws IOException {
		StringBuilder b = new StringBuilder();
		int c;
		c = in.read();
		while (c >= 0 && c <= ' ')
			c = in.read();
		if (c < 0)
			return "";
		while (c > ' ') {
			b.append((char) c);
			c = in.read();
		}
		return b.toString();
	}

	public static void main(String[] args) {
		try {
			Locale.setDefault(Locale.US);
		} catch (Exception e) {

		}
		// new Thread(new Main()).start();
		new Thread(null, new Main(), "1", 1 << 25).start();
	}
}

/**
 * Problem:
 * Source:
 * Link:
 *
 * @author Alexei Ostrovski
 */

import java.io.*;
import java.util.*;

public class SpidersSolver {

	public static final boolean DEBUG = false;

	public static void main(String[] args) {
		//redirect input and output
		if (DEBUG)
		{
			try {
				System.setIn(new FileInputStream("input.txt"));
				//System.setOut(new PrintStream("input.txt"));
			} catch (IOException e) {
				//nothing
			}
		}
		Scanner sc = new Scanner(System.in);

		//read data
		int n = sc.nextInt(), m = sc.nextInt();

		//solve
		if (n < m) {
			int tmp = n;
			n = m;
			m = tmp;
		}
		// Now (n >= m) && (m <= 6)
		int pow = 1;
		for (int i = 0; i < m; i++)
			pow *= 2;

		int[] count = new int[pow];
		for (int cur = 0; cur < pow; cur++)
		{
			int x = cur;
			while (x > 0)
			{
				count[cur] += (x % 2);
				x /= 2;
			}
			count[cur] = m - count[cur];
		}
		//System.out.println(Arrays.toString(count));

		int[][] C = new int[pow][pow];
		for (int cur = 0; cur < pow; cur++)
		{
			C[0][cur] = 0;//count[cur];
			for (int last = 1; last < pow; last++)
				C[last][cur] = Integer.MIN_VALUE;
		}

		for (int i = 0; i < n; i++)
		{
			int[][] newC = new int[pow][pow];

			for (int cur = 0; cur < pow; cur++)
				for (int next = 0; next < pow; next++)
				{
					int mask = cur | (cur << 1) | (cur >> 1) | next;
					mask %= pow;

					int max = 0;
					for (int last = 0; last < pow; last++)
						if (((last | mask) == pow - 1) && (max < count[cur] + C[last][cur]))
							max = count[cur] + C[last][cur];

					newC[cur][next] = max;
				}
			C = newC;
		}

		int result = 0;
		for (int cur = 0; cur < pow; cur++)
			result = Math.max(result, C[cur][0]);

		//output
		System.out.println(result);
	}
}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;

public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;
	double EPS=1e-9;

	int h, w;

	void run(){
		h=sc.nextInt();
		w=sc.nextInt();
		solve();
	}

	void shuffle(int[] is){
		Random rand=new Random();
		for(int i=is.length-1; i>=1; i--){
			int j=rand.nextInt(i+1);
			int t=is[i];
			is[i]=is[j];
			is[j]=t;
		}
	}

	void solve(){
		n=w*h;
		g=new long[n];
		int[] dx={0, 0, -1, 1};
		int[] dy={-1, 1, 0, 0};
		for(int y=0; y<h; y++){
			for(int x=0; x<w; x++){
				for(int k=0; k<4; k++){
					int x2=x+dx[k];
					int y2=y+dy[k];
					if(x2>=0&&x2<w&&y2>=0&&y2<h){
						g[y*w+x]|=1L<<(y2*w+x2);
					}
				}
			}
		}
		candidate=new int[n];
		xs=new Xorshift();
		mds=(1L<<n)-1;
		mds(0, 0, 0);
		println((n-Long.bitCount(mds))+"");
	}

	int n;
	long[] g;
	long mds;
	int[] candidate;
	Xorshift xs;

	void mds(long choosed, long removed, long covered){
		if(Long.bitCount(choosed)>=Long.bitCount(mds))
			return;
		if(covered==((1L<<n)-1)){
			if(Long.bitCount(choosed)<Long.bitCount(mds))
				mds=choosed;
			return;
		}

		{
			long s=covered;
			for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
				int i=Long.numberOfTrailingZeros(remained);
				s|=(1L<<i)|g[i];
			}
			if(s!=((1L<<n)-1)){
				return;
			}
		}

		int index=0;
		int k=-1;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			if((covered>>>i&1)==1){
				if(Long.bitCount(g[i]&~covered)==0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}else if(Long.bitCount(g[i]&~covered)==1
						&&(g[i]&~covered&~removed)!=0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}
			}else{
				if(Long.bitCount(g[i]&~removed)==0){
					mds(choosed|(1L<<i), removed|(1L<<i), covered|(1L<<i)|g[i]);
					return;
				}else if(Long.bitCount(g[i]&~removed)==1
						&&((g[i]&~removed)|(g[i]&~covered))==(g[i]&~removed)){
					int j=Long.numberOfTrailingZeros(g[i]&~removed);
					mds(choosed|(1L<<j), removed|(1L<<i)|(1L<<j), covered
							|(1L<<j)|g[j]);
					return;
				}
			}
			// if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered))
			// k=i;
			if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered)){
				index=0;
				candidate[index++]=i;
				k=i;
			}else if(Long.bitCount(g[i]&~covered)==Long.bitCount(g[k]&~covered)){
				candidate[index++]=i;
			}
		}
		if(k==-1)
			return;

		// k=candidate[xs.nextInt(index)];

		mds(choosed|(1L<<k), removed|(1L<<k), covered|(1L<<k)|g[k]);
		mds(choosed, removed|(1L<<k), covered);
	}

	class Xorshift{

		int x, y, z, w;

		public Xorshift(){
			x=123456789;
			y=362436069;
			z=521288629;
			w=88675123;
		}

		public Xorshift(int seed){
			x=_(seed, 0);
			y=_(x, 1);
			z=_(y, 2);
			w=_(z, 3);
		}

		int _(int s, int i){
			return 1812433253*(s^(s>>>30))+i+1;
		}

		// 32bit signed
		public int nextInt(){
			int t=x^(x<<11);
			x=y;
			y=z;
			z=w;
			return w=w^(w>>>19)^t^(t>>>8);
		}

		// error = O(n*2^-32)
		public int nextInt(int n){
			return (int)(n*nextDouble());
		}

		// [0, 1) (53bit)
		public double nextDouble(){
			int a=nextInt()>>>5, b=nextInt()>>>6;
			return (a*67108864.0+b)*(1.0/(1L<<53));
		}

	}

	void println(String s){
		System.out.println(s);
	}

	void print(String s){
		System.out.print(s);
	}

	void debug(Object... os){
		System.err.println(Arrays.deepToString(os));
	}

	public static void main(String[] args){
		Locale.setDefault(Locale.US);
		new P111C().run();
	}

}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;

public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;
	double EPS=1e-9;

	int h, w;

	void run(){
		h=sc.nextInt();
		w=sc.nextInt();
		solve();
	}

	void shuffle(int[] is){
		Random rand=new Random();
		for(int i=is.length-1; i>=1; i--){
			int j=rand.nextInt(i+1);
			int t=is[i];
			is[i]=is[j];
			is[j]=t;
		}
	}

	void solve(){
		n=w*h;
		g=new long[n];
		int[] dx={0, 0, -1, 1};
		int[] dy={-1, 1, 0, 0};
		for(int y=0; y<h; y++){
			for(int x=0; x<w; x++){
				for(int k=0; k<4; k++){
					int x2=x+dx[k];
					int y2=y+dy[k];
					if(x2>=0&&x2<w&&y2>=0&&y2<h){
						g[y*w+x]|=1L<<(y2*w+x2);
					}
				}
			}
		}
		candidate=new int[n];
		mds=(1L<<n)-1;
		mds(0, 0, 0);
		println((n-Long.bitCount(mds))+"");
	}

	int n;
	long[] g;
	long mds;
	int[] candidate;

	void mds(long choosed, long removed, long covered){
		if(Long.bitCount(choosed)>=Long.bitCount(mds))
			return;

		if(covered==((1L<<n)-1)){
			if(Long.bitCount(choosed)<Long.bitCount(mds))
				mds=choosed;
			return;
		}

		long s=covered;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			s|=(1L<<i)|g[i];
		}
		if(s!=((1L<<n)-1))
			return;

		int k=-1;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			if((covered>>>i&1)==1){
				if(Long.bitCount(g[i]&~covered)==0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}else if(Long.bitCount(g[i]&~covered)==1
						&&(g[i]&~covered&~removed)!=0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}
			}else{
				if(Long.bitCount(g[i]&~removed)==0){
					mds(choosed|(1L<<i), removed|(1L<<i), covered|(1L<<i)|g[i]);
					return;
				}else if(Long.bitCount(g[i]&~removed)==1
						&&((g[i]&~removed)|(g[i]&~covered))==(g[i]&~removed)){
					int j=Long.numberOfTrailingZeros(g[i]&~removed);
					mds(choosed|(1L<<j), removed|(1L<<i)|(1L<<j), covered
							|(1L<<j)|g[j]);
					return;
				}
			}
			if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered))
				k=i;
		}
		if(k==-1)
			return;

		mds(choosed|(1L<<k), removed|(1L<<k), covered|(1L<<k)|g[k]);
		mds(choosed, removed|(1L<<k), covered);
	}

	void println(String s){
		System.out.println(s);
	}

	void print(String s){
		System.out.print(s);
	}

	void debug(Object... os){
		System.err.println(Arrays.deepToString(os));
	}

	public static void main(String[] args){
		Locale.setDefault(Locale.US);
		new P111C().run();
	}

}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;

// Petya and Spiders
// 2012/08/16
public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;
	double EPS=1e-9;

	int h, w;

	void run(){
		h=sc.nextInt();
		w=sc.nextInt();
		solve();
	}

	void solve(){
		n=w*h;
		g=new long[n];
		int[] dx={0, 0, -1, 1};
		int[] dy={-1, 1, 0, 0};
		for(int y=0; y<h; y++){
			for(int x=0; x<w; x++){
				for(int k=0; k<4; k++){
					int x2=x+dx[k];
					int y2=y+dy[k];
					if(x2>=0&&x2<w&&y2>=0&&y2<h){
						g[y*w+x]|=1L<<(y2*w+x2);
					}
				}
			}
		}
		mds=(1L<<n)-1;
		mds(0, 0, 0);
		println((n-Long.bitCount(mds))+"");
	}

	int n;
	long[] g;
	long mds;

	void mds(long choosed, long removed, long covered){
		if(Long.bitCount(choosed)>=Long.bitCount(mds))
			return;

		if(covered==((1L<<n)-1)){
			mds=choosed;
			return;
		}

		long s=covered;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			s|=(1L<<i)|g[i];
		}
		if(s!=((1L<<n)-1))
			return;

		int k=-1;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			if((covered>>>i&1)==1){
				if(Long.bitCount(g[i]&~covered)==0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}else if(Long.bitCount(g[i]&~covered)==1
						&&(g[i]&~covered&~removed)!=0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}
			}else{
				if(Long.bitCount(g[i]&~removed)==0){
					mds(choosed|(1L<<i), removed|(1L<<i), covered|(1L<<i)|g[i]);
					return;
				}else if(Long.bitCount(g[i]&~removed)==1
						&&((g[i]&~removed)|(g[i]&~covered))==(g[i]&~removed)){
					int j=Long.numberOfTrailingZeros(g[i]&~removed);
					mds(choosed|(1L<<j), removed|(1L<<i)|(1L<<j), covered
							|(1L<<j)|g[j]);
					return;
				}
			}
			if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered))
				k=i;
		}
		if(k==-1)
			return;

		mds(choosed|(1L<<k), removed|(1L<<k), covered|(1L<<k)|g[k]);
		mds(choosed, removed|(1L<<k), covered);
	}

	void println(String s){
		System.out.println(s);
	}

	void debug(Object... os){
		System.err.println(Arrays.deepToString(os));
	}

	public static void main(String[] args){
		Locale.setDefault(Locale.US);
		new P111C().run();
	}

}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;

public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;
	double EPS=1e-9;

	int h, w;

	void run(){
		h=sc.nextInt();
		w=sc.nextInt();
		solve();
	}

	void solve(){
		n=w*h;
		g=new long[n];
		int[] dx={0, 0, -1, 1};
		int[] dy={-1, 1, 0, 0};
		for(int y=0; y<h; y++){
			for(int x=0; x<w; x++){
				for(int k=0; k<4; k++){
					int x2=x+dx[k];
					int y2=y+dy[k];
					if(x2>=0&&x2<w&&y2>=0&&y2<h){
						g[y*w+x]|=1L<<(y2*w+x2);
					}
				}
			}
		}
		mds=(1L<<n)-1;
		mds(0, 0, 0);
		println((n-Long.bitCount(mds))+"");
	}

	int n;
	long[] g;
	long mds;

	void mds(long choosed, long removed, long covered){
		if(Long.bitCount(choosed)>=Long.bitCount(mds))
			return;
		if(covered==((1L<<n)-1)){
			if(Long.bitCount(choosed)<Long.bitCount(mds))
				mds=choosed;
			return;
		}

		int k=-1;
		ArrayList<Integer> list=new ArrayList<Integer>();
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			if((covered>>>i&1)==1){
				if(Long.bitCount(g[i]&~covered)==0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}else if(Long.bitCount(g[i]&~covered)==1
						&&(g[i]&~covered&~removed)!=0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}
			}else{
				if(Long.bitCount(g[i]&~removed)==0){
					mds(choosed|(1L<<i), removed|(1L<<i), covered|(1L<<i)|g[i]);
					return;
				}else if(Long.bitCount(g[i]&~removed)==1
						&&((g[i]&~removed)|(g[i]&~covered))==(g[i]&~removed)){
					int j=Long.numberOfTrailingZeros(g[i]&~removed);
					mds(choosed|(1L<<j), removed|(1L<<i)|(1L<<j), covered
							|(1L<<j)|g[j]);
					return;
				}
			}
			// if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered))
			// k=i;
			if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered)){
				list.clear();
				list.add(i);
				k=i;
			}else if(Long.bitCount(g[i]&~covered)==Long.bitCount(g[k]&~covered))
				list.add(i);
		}
		if(k==-1)
			return;

		k=list.get((int)(list.size()*random()));

		mds(choosed|(1L<<k), removed|(1L<<k), covered|(1L<<k)|g[k]);
		mds(choosed, removed|(1L<<k), covered);
	}

	void println(String s){
		System.out.println(s);
	}

	void print(String s){
		System.out.print(s);
	}

	void debug(Object... os){
		System.err.println(Arrays.deepToString(os));
	}

	public static void main(String[] args){
		Locale.setDefault(Locale.US);
		new P111C().run();
	}
}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;

import static java.lang.Math.*;
import static java.util.Arrays.*;

// Petya and Spiders
// 2011/9/3
public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;

	int n, m;

	void run(){
		n=sc.nextInt();
		m=sc.nextInt();
		solve();
	}

	void solve(){
		if(n<m){
			int t=n;
			n=m;
			m=t;
		}
		int full=(1<<m)-1;
		int[][] dp=new int[1<<m][1<<m];
		int[][] tmp=new int[1<<m][1<<m];
		for(int i=0; i<1<<m; i++){
			fill(dp[i], INF);
		}
		for(int i=0; i<1<<m; i++){
			int b1=(i|(i>>1)|(i<<1))&full;
			int b2=i;
			dp[b1][b2]=Integer.bitCount(i);
		}
		for(int j=0; j<n-1; j++){
			for(int i=0; i<1<<m; i++){
				System.arraycopy(dp[i], 0, tmp[i], 0, 1<<m);
				fill(dp[i], INF);
			}

			for(int b1=0; b1<1<<m; b1++){
				for(int b2=0; b2<1<<m; b2++){
					for(int i=0; i<1<<m; i++){
						if((b1|i)!=full){
							continue;
						}
						int b=(i|(i>>1)|(i<<1))&full;
						dp[b2|b][i]=min(dp[b2|b][i],
								tmp[b1][b2]+Integer.bitCount(i));
					}
				}
			}
		}

		int min=INF;
		for(int i=0; i<1<<m; i++){
			min=min(min, dp[full][i]);
		}
		int ans=m*n-min;
		println(ans+"");
	}

	void println(String s){
		System.out.println(s);
	}

	public static void main(String[] args){
		new P111C().run();
	}
}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;

public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;
	double EPS=1e-9;

	int h, w;

	void run(){
		h=sc.nextInt();
		w=sc.nextInt();
		solve();
	}

	void shuffle(int[] is){
		Random rand=new Random();
		for(int i=is.length-1; i>=1; i--){
			int j=rand.nextInt(i+1);
			int t=is[i];
			is[i]=is[j];
			is[j]=t;
		}
	}

	void solve(){
		n=w*h;
		g=new long[n];
		int[] dx={0, 0, -1, 1};
		int[] dy={-1, 1, 0, 0};
		for(int y=0; y<h; y++){
			for(int x=0; x<w; x++){
				for(int k=0; k<4; k++){
					int x2=x+dx[k];
					int y2=y+dy[k];
					if(x2>=0&&x2<w&&y2>=0&&y2<h){
						g[y*w+x]|=1L<<(y2*w+x2);
					}
				}
			}
		}
		candidate=new int[n];
		xs=new Xorshift();
		mds=(1L<<n)-1;
		mds(0, 0, 0);
		println((n-Long.bitCount(mds))+"");
	}

	int n;
	long[] g;
	long mds;
	int[] candidate;
	Xorshift xs;

	void mds(long choosed, long removed, long covered){
		if(Long.bitCount(choosed)>=Long.bitCount(mds))
			return;
		if(covered==((1L<<n)-1)){
			if(Long.bitCount(choosed)<Long.bitCount(mds))
				mds=choosed;
			return;
		}

		{
			long s=covered;
			for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
				int i=Long.numberOfTrailingZeros(remained);
				s|=(1L<<i)|g[i];
			}
			if(s!=((1L<<n)-1)){
				return;
			}
		}

//		int index=0;
		int k=-1;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			if((covered>>>i&1)==1){
				if(Long.bitCount(g[i]&~covered)==0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}else if(Long.bitCount(g[i]&~covered)==1
						&&(g[i]&~covered&~removed)!=0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}
			}else{
				if(Long.bitCount(g[i]&~removed)==0){
					mds(choosed|(1L<<i), removed|(1L<<i), covered|(1L<<i)|g[i]);
					return;
				}else if(Long.bitCount(g[i]&~removed)==1
						&&((g[i]&~removed)|(g[i]&~covered))==(g[i]&~removed)){
					int j=Long.numberOfTrailingZeros(g[i]&~removed);
					mds(choosed|(1L<<j), removed|(1L<<i)|(1L<<j), covered
							|(1L<<j)|g[j]);
					return;
				}
			}
			 if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered))
			 k=i;
			 /*
			if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered)){
				index=0;
				candidate[index++]=i;
				k=i;
			}else if(Long.bitCount(g[i]&~covered)==Long.bitCount(g[k]&~covered)){
				candidate[index++]=i;
			}
			*/
		}
		if(k==-1)
			return;

		// k=candidate[xs.nextInt(index)];

		mds(choosed|(1L<<k), removed|(1L<<k), covered|(1L<<k)|g[k]);
		mds(choosed, removed|(1L<<k), covered);
	}

	class Xorshift{

		int x, y, z, w;

		public Xorshift(){
			x=123456789;
			y=362436069;
			z=521288629;
			w=88675123;
		}

		public Xorshift(int seed){
			x=_(seed, 0);
			y=_(x, 1);
			z=_(y, 2);
			w=_(z, 3);
		}

		int _(int s, int i){
			return 1812433253*(s^(s>>>30))+i+1;
		}

		// 32bit signed
		public int nextInt(){
			int t=x^(x<<11);
			x=y;
			y=z;
			z=w;
			return w=w^(w>>>19)^t^(t>>>8);
		}

		// error = O(n*2^-32)
		public int nextInt(int n){
			return (int)(n*nextDouble());
		}

		// [0, 1) (53bit)
		public double nextDouble(){
			int a=nextInt()>>>5, b=nextInt()>>>6;
			return (a*67108864.0+b)*(1.0/(1L<<53));
		}

	}

	void println(String s){
		System.out.println(s);
	}

	void print(String s){
		System.out.print(s);
	}

	void debug(Object... os){
		System.err.println(Arrays.deepToString(os));
	}

	public static void main(String[] args){
		Locale.setDefault(Locale.US);
		new P111C().run();
	}

}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;

public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;
	double EPS=1e-9;

	int h, w;

	void run(){
		h=sc.nextInt();
		w=sc.nextInt();
		solve();
	}

	void shuffle(int[] is){
		Random rand=new Random();
		for(int i=is.length-1; i>=1; i--){
			int j=rand.nextInt(i+1);
			int t=is[i];
			is[i]=is[j];
			is[j]=t;
		}
	}

	void solve(){
		n=w*h;
		g=new long[n];
		int[] dx={0, 0, -1, 1};
		int[] dy={-1, 1, 0, 0};
		for(int y=0; y<h; y++){
			for(int x=0; x<w; x++){
				for(int k=0; k<4; k++){
					int x2=x+dx[k];
					int y2=y+dy[k];
					if(x2>=0&&x2<w&&y2>=0&&y2<h){
						g[y*w+x]|=1L<<(y2*w+x2);
					}
				}
			}
		}
		candidate=new int[n];
		xs=new Xorshift();
		mds=(1L<<n)-1;
		mds(0, 0, 0);
		println((n-Long.bitCount(mds))+"");
	}

	int n;
	long[] g;
	long mds;
	int[] candidate;
	Xorshift xs;

	void mds(long choosed, long removed, long covered){
		if(Long.bitCount(choosed)>=Long.bitCount(mds))
			return;
		if(covered==((1L<<n)-1)){
			if(Long.bitCount(choosed)<Long.bitCount(mds))
				mds=choosed;
			return;
		}

		int index=0;
		int k=-1;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			if((covered>>>i&1)==1){
				if(Long.bitCount(g[i]&~covered)==0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}else if(Long.bitCount(g[i]&~covered)==1
						&&(g[i]&~covered&~removed)!=0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}
			}else{
				if(Long.bitCount(g[i]&~removed)==0){
					mds(choosed|(1L<<i), removed|(1L<<i), covered|(1L<<i)|g[i]);
					return;
				}else if(Long.bitCount(g[i]&~removed)==1
						&&((g[i]&~removed)|(g[i]&~covered))==(g[i]&~removed)){
					int j=Long.numberOfTrailingZeros(g[i]&~removed);
					mds(choosed|(1L<<j), removed|(1L<<i)|(1L<<j), covered
							|(1L<<j)|g[j]);
					return;
				}
			}
			// if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered))
			// k=i;
			if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered)){
				index=0;
				candidate[index++]=i;
				k=i;
			}else if(Long.bitCount(g[i]&~covered)==Long.bitCount(g[k]&~covered)){
				candidate[index++]=i;
			}
		}
		if(k==-1)
			return;

		k=candidate[xs.nextInt(index)];

		mds(choosed|(1L<<k), removed|(1L<<k), covered|(1L<<k)|g[k]);
		mds(choosed, removed|(1L<<k), covered);
	}

	class Xorshift{

		int x, y, z, w;

		public Xorshift(){
			x=123456789;
			y=362436069;
			z=521288629;
			w=88675123;
		}

		public Xorshift(int seed){
			x=_(seed, 0);
			y=_(x, 1);
			z=_(y, 2);
			w=_(z, 3);
		}

		int _(int s, int i){
			return 1812433253*(s^(s>>>30))+i+1;
		}

		// 32bit signed
		public int nextInt(){
			int t=x^(x<<11);
			x=y;
			y=z;
			z=w;
			return w=w^(w>>>19)^t^(t>>>8);
		}

		// error = O(n*2^-32)
		public int nextInt(int n){
			return (int)(n*nextDouble());
		}

		// [0, 1) (53bit)
		public double nextDouble(){
			int a=nextInt()>>>5, b=nextInt()>>>6;
			return (a*67108864.0+b)*(1.0/(1L<<53));
		}

	}

	void println(String s){
		System.out.println(s);
	}

	void print(String s){
		System.out.print(s);
	}

	void debug(Object... os){
		System.err.println(Arrays.deepToString(os));
	}

	public static void main(String[] args){
		Locale.setDefault(Locale.US);
		new P111C().run();
	}

}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;

public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;
	double EPS=1e-9;

	int h, w;

	void run(){
		h=sc.nextInt();
		w=sc.nextInt();
		solve();
	}

	void shuffle(int[] is){
		Random rand=new Random();
		for(int i=is.length-1; i>=1; i--){
			int j=rand.nextInt(i+1);
			int t=is[i];
			is[i]=is[j];
			is[j]=t;
		}
	}

	void solve(){
		n=w*h;
		g=new long[n];
		int[] dx={0, 0, -1, 1};
		int[] dy={-1, 1, 0, 0};
		for(int y=0; y<h; y++){
			for(int x=0; x<w; x++){
				for(int k=0; k<4; k++){
					int x2=x+dx[k];
					int y2=y+dy[k];
					if(x2>=0&&x2<w&&y2>=0&&y2<h){
						g[y*w+x]|=1L<<(y2*w+x2);
					}
				}
			}
		}
		candidate=new int[n];
		xs=new Xorshift();
		mds=(1L<<n)-1;
		mds(0, 0, 0);
		println((n-Long.bitCount(mds))+"");
	}

	int n;
	long[] g;
	long mds;
	int[] candidate;
	Xorshift xs;

	void mds(long choosed, long removed, long covered){
		if(Long.bitCount(choosed)>=Long.bitCount(mds))
			return;
		if(covered==((1L<<n)-1)){
			if(Long.bitCount(choosed)<Long.bitCount(mds))
				mds=choosed;
			return;
		}

		{
			long s=covered;
			for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
				int i=Long.numberOfTrailingZeros(remained);
				s|=(1L<<i)|g[i];
			}
			if(s!=((1L<<n)-1)){
				return;
			}
		}

		int index=0;
		int k=-1;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			if((covered>>>i&1)==1){
				if(Long.bitCount(g[i]&~covered)==0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}else if(Long.bitCount(g[i]&~covered)==1
						&&(g[i]&~covered&~removed)!=0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}
			}else{
				if(Long.bitCount(g[i]&~removed)==0){
					mds(choosed|(1L<<i), removed|(1L<<i), covered|(1L<<i)|g[i]);
					return;
				}else if(Long.bitCount(g[i]&~removed)==1
						&&((g[i]&~removed)|(g[i]&~covered))==(g[i]&~removed)){
					int j=Long.numberOfTrailingZeros(g[i]&~removed);
					mds(choosed|(1L<<j), removed|(1L<<i)|(1L<<j), covered
							|(1L<<j)|g[j]);
					return;
				}
			}
			// if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered))
			// k=i;
			if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered)){
				index=0;
				candidate[index++]=i;
				k=i;
			}else if(Long.bitCount(g[i]&~covered)==Long.bitCount(g[k]&~covered)){
				candidate[index++]=i;
			}
		}
		if(k==-1)
			return;

		k=candidate[xs.nextInt(index)];

		mds(choosed|(1L<<k), removed|(1L<<k), covered|(1L<<k)|g[k]);
		mds(choosed, removed|(1L<<k), covered);
	}

	class Xorshift{

		int x, y, z, w;

		public Xorshift(){
			x=123456789;
			y=362436069;
			z=521288629;
			w=88675123;
		}

		public Xorshift(int seed){
			x=_(seed, 0);
			y=_(x, 1);
			z=_(y, 2);
			w=_(z, 3);
		}

		int _(int s, int i){
			return 1812433253*(s^(s>>>30))+i+1;
		}

		// 32bit signed
		public int nextInt(){
			int t=x^(x<<11);
			x=y;
			y=z;
			z=w;
			return w=w^(w>>>19)^t^(t>>>8);
		}

		// error = O(n*2^-32)
		public int nextInt(int n){
			return (int)(n*nextDouble());
		}

		// [0, 1) (53bit)
		public double nextDouble(){
			int a=nextInt()>>>5, b=nextInt()>>>6;
			return (a*67108864.0+b)*(1.0/(1L<<53));
		}

	}

	void println(String s){
		System.out.println(s);
	}

	void print(String s){
		System.out.print(s);
	}

	void debug(Object... os){
		System.err.println(Arrays.deepToString(os));
	}

	public static void main(String[] args){
		Locale.setDefault(Locale.US);
		new P111C().run();
	}

}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;

public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;
	double EPS=1e-9;

	int h, w;

	void run(){
		h=sc.nextInt();
		w=sc.nextInt();
		solve();
	}

	void shuffle(int[] is){
		Random rand=new Random();
		for(int i=is.length-1; i>=1; i--){
			int j=rand.nextInt(i+1);
			int t=is[i];
			is[i]=is[j];
			is[j]=t;
		}
	}

	void solve(){
		n=w*h;
		g=new long[n];
		int[] dx={0, 0, -1, 1};
		int[] dy={-1, 1, 0, 0};
		int[] _=new int[n];
		for(int i=0; i<n; i++){
			_[i]=i;
		}
		shuffle(_);
		HashMap<Integer, Integer> map=new HashMap<Integer, Integer>();
		for(int i=0; i<n; i++){
			map.put(_[i], i);
		}
		for(int y=0; y<h; y++){
			for(int x=0; x<w; x++){
				for(int k=0; k<4; k++){
					int x2=x+dx[k];
					int y2=y+dy[k];
					if(x2>=0&&x2<w&&y2>=0&&y2<h){
						// g[y*w+x]|=1L<<(y2*w+x2);
						g[map.get(y*w+x)]|=1L<<(map.get(y2*w+x2));
					}
				}
			}
		}
		mds=(1L<<n)-1;
		mds(0, 0, 0);
		println((n-Long.bitCount(mds))+"");
	}

	int n;
	long[] g;
	long mds;
	long[] candidate;

	void mds(long choosed, long removed, long covered){
		if(Long.bitCount(choosed)>=Long.bitCount(mds))
			return;
		if(covered==((1L<<n)-1)){
			if(Long.bitCount(choosed)<Long.bitCount(mds))
				mds=choosed;
			return;
		}

		int k=-1;
		// ArrayList<Integer> list=new ArrayList<Integer>();
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			if((covered>>>i&1)==1){
				if(Long.bitCount(g[i]&~covered)==0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}else if(Long.bitCount(g[i]&~covered)==1
						&&(g[i]&~covered&~removed)!=0){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}
			}else{
				if(Long.bitCount(g[i]&~removed)==0){
					mds(choosed|(1L<<i), removed|(1L<<i), covered|(1L<<i)|g[i]);
					return;
				}else if(Long.bitCount(g[i]&~removed)==1
						&&((g[i]&~removed)|(g[i]&~covered))==(g[i]&~removed)){
					int j=Long.numberOfTrailingZeros(g[i]&~removed);
					mds(choosed|(1L<<j), removed|(1L<<i)|(1L<<j), covered
							|(1L<<j)|g[j]);
					return;
				}
			}
			if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered))
				k=i;
			if(false){
				/*
				if(k==-1
						||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]
								&~covered)){
					list.clear();
					list.add(i);
					k=i;
				}else if(Long.bitCount(g[i]&~covered)==Long.bitCount(g[k]
						&~covered))
					list.add(i);
					*/
			}
		}
		if(k==-1)
			return;

		// k=list.get((int)(list.size()*random()));

		mds(choosed|(1L<<k), removed|(1L<<k), covered|(1L<<k)|g[k]);
		mds(choosed, removed|(1L<<k), covered);
	}

	void println(String s){
		System.out.println(s);
	}

	void print(String s){
		System.out.print(s);
	}

	void debug(Object... os){
		System.err.println(Arrays.deepToString(os));
	}

	public static void main(String[] args){
		Locale.setDefault(Locale.US);
		new P111C().run();
	}
}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;
import static java.lang.Math.*;
import static java.util.Arrays.*;
import static java.util.Collections.*;

// Petya and Spiders
// 2012/08/15
public class P111C{
	Scanner sc=new Scanner(System.in);

	int INF=1<<28;
	double EPS=1e-9;

	int h, w;

	void run(){
		h=sc.nextInt();
		w=sc.nextInt();
		solve();
	}

	void solve(){
		n=w*h;
		g=new long[n];
		int[] dx={0, 0, -1, 1};
		int[] dy={-1, 1, 0, 0};
		for(int y=0; y<h; y++){
			for(int x=0; x<w; x++){
				for(int k=0; k<4; k++){
					int x2=x+dx[k];
					int y2=y+dy[k];
					if(x2>=0&&x2<w&&y2>=0&&y2<h){
						g[y*w+x]|=1L<<(y2*w+x2);
					}
				}
			}
		}
		mds=(1L<<n)-1;
		mds(0, 0, 0);
		println((n-Long.bitCount(mds))+"");
	}

	int n;
	long[] g;
	long mds;

	void mds(long choosed, long removed, long covered){
		if(covered==((1L<<n)-1)){
			mds=choosed;
			return;
		}
		if(Long.bitCount(choosed)>=Long.bitCount(mds)-1)
			return;
		long s=covered;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			s|=(1L<<i)|g[i];
		}
		if(s!=((1L<<n)-1))
			return;

		int k=-1;
		for(long remained=~removed&((1L<<n)-1); remained!=0; remained&=remained-1){
			int i=Long.numberOfTrailingZeros(remained);
			if((covered>>>i&1)==1){
				if(Long.bitCount(g[i]&~covered)<=1){
					mds(choosed, removed|(1L<<i), covered);
					return;
				}
			}else{
				if(Long.bitCount(g[i]&~removed)==0){
					mds(choosed|(1L<<i), removed|(1L<<i), covered|(1L<<i)|g[i]);
					return;
				}else if(Long.bitCount(g[i]&~removed)==1
						&&(g[i]&(~removed|~covered))==(g[i]&~removed)){
					int j=Long.numberOfTrailingZeros(g[i]&~removed);
					mds(choosed|(1L<<j), removed|(1L<<i)|(1L<<j), covered
							|(1L<<j)|g[j]);
					return;
				}
			}
			if(k==-1||Long.bitCount(g[i]&~covered)>Long.bitCount(g[k]&~covered))
				k=i;
		}
		if(k==-1)
			return;
		mds(choosed|(1L<<k), removed|(1L<<k), covered|(1L<<k)|g[k]);
		mds(choosed, removed|(1L<<k), covered);
	}

	void println(String s){
		System.out.println(s);
	}

	void debug(Object... os){
		System.err.println(Arrays.deepToString(os));
	}

	public static void main(String[] args){
		Locale.setDefault(Locale.US);
		new P111C().run();
	}

}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;

import static java.lang.Math.*;
import static java.util.Arrays.*;

public class C{
    Scanner sc=new Scanner(System.in);

    int INF=1<<28;
    double EPS=1e-9;

    int n, m;

    void run(){
        n=sc.nextInt();
        m=sc.nextInt();
        solve();
    }

    void solve(){
        if(n<m){
            int t=n;
            n=m;
            m=t;
        }
        int full=(1<<m)-1;
        int[][] dp=new int[1<<m][1<<m];
        int[][] tmp=new int[1<<m][1<<m];
        for(int i=0; i<1<<m; i++){
            fill(dp[i], INF);
        }
        for(int i=0; i<1<<m; i++){
            int b1=(i|(i>>1)|(i<<1))&full;
            int b2=i;
            dp[b1][b2]=Integer.bitCount(i);
            debug(Integer.toBinaryString(b1), dp[b1]);
        }
        debug();
        for(int j=0; j<n-1; j++){
            for(int i=0; i<1<<m; i++){
                System.arraycopy(dp[i], 0, tmp[i], 0, 1<<m);
                fill(dp[i], INF);
            }

            for(int b1=0; b1<1<<m; b1++){
                for(int b2=0; b2<1<<m; b2++){
                    for(int i=0; i<1<<m; i++){
                        if((b1|i)!=full){
                            continue;
                        }
                        if(false)
                            debug(Integer.toBinaryString(b1),
                                    Integer.toBinaryString(b2),
                                    Integer.toBinaryString(i));
                        int b=(i|(i>>1)|(i<<1))&full;
                        dp[b2|b][i]=min(dp[b2|b][i],
                                tmp[b1][b2]+Integer.bitCount(i));
                    }
                }
            }
            debug(j);
            for(int i=0; i<1<<m; i++){
                debug(Integer.toBinaryString(i), dp[i]);
            }
        }

        int min=INF;
        for(int i=0; i<1<<m; i++){
            min=min(min, dp[full][i]);
        }
        debug(min);
        int ans=m*n-min;
        debug("ans",ans);
        println(ans+"");
    }

    void println(String s){
        System.out.println(s);
    }

    void print(String s){
        System.out.print(s);
    }

    void debug(Object... os){
        // System.err.println(Arrays.deepToString(os));
    }

    public static void main(String[] args){
        new C().run();
    }
}

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.StringTokenizer;

public class CodeJ
{
	static class Scanner
	{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer("");

		public String nextLine()
		{
			try
			{
				return br.readLine();
			}
			catch(Exception e)
			{
				throw(new RuntimeException());
			}
		}

		public String next()
		{
			while(!st.hasMoreTokens())
			{
				String l = nextLine();
				if(l == null)
					return null;
				st = new StringTokenizer(l);
			}
			return st.nextToken();
		}

		public int nextInt()
		{
			return Integer.parseInt(next());
		}

		public long nextLong()
		{
			return Long.parseLong(next());
		}

		public double nextDouble()
		{
			return Double.parseDouble(next());
		}

		public int[] nextIntArray(int n)
		{
			int[] res = new int[n];
			for(int i = 0; i < res.length; i++)
				res[i] = nextInt();
			return res;
		}

		public long[] nextLongArray(int n)
		{
			long[] res = new long[n];
			for(int i = 0; i < res.length; i++)
				res[i] = nextLong();
			return res;
		}

		public double[] nextDoubleArray(int n)
		{
			double[] res = new double[n];
			for(int i = 0; i < res.length; i++)
				res[i] = nextLong();
			return res;
		}
		public void sortIntArray(int[] array)
		{
			Integer[] vals = new Integer[array.length];
			for(int i = 0; i < array.length; i++)
				vals[i] = array[i];
			Arrays.sort(vals);
			for(int i = 0; i < array.length; i++)
				array[i] = vals[i];
		}

		public void sortLongArray(long[] array)
		{
			Long[] vals = new Long[array.length];
			for(int i = 0; i < array.length; i++)
				vals[i] = array[i];
			Arrays.sort(vals);
			for(int i = 0; i < array.length; i++)
				array[i] = vals[i];
		}

		public void sortDoubleArray(double[] array)
		{
			Double[] vals = new Double[array.length];
			for(int i = 0; i < array.length; i++)
				vals[i] = array[i];
			Arrays.sort(vals);
			for(int i = 0; i < array.length; i++)
				array[i] = vals[i];
		}
	}

	static int nFilas;
	static int nColumnas;

	static byte[][][][][] dp;

	static byte dp(int mascaraActual, int enviadosActual, int enviadosSiguiente, int filaActual, int columnaActual)
	{
		if(dp[mascaraActual][enviadosActual][enviadosSiguiente][filaActual][columnaActual] != Byte.MAX_VALUE)
			return dp[mascaraActual][enviadosActual][enviadosSiguiente][filaActual][columnaActual];
		if(filaActual == nFilas)
			return dp[mascaraActual][enviadosActual][enviadosSiguiente][filaActual][columnaActual] = 0;
		if(columnaActual == nColumnas)
		{
			int ambos = mascaraActual & enviadosSiguiente;
			int mascaraSiguiente = (1 << nColumnas) - 1;
			mascaraSiguiente ^= ambos;
			int mascaraEnviados = enviadosSiguiente;
			mascaraEnviados ^= ambos;
			return dp[mascaraActual][enviadosActual][enviadosSiguiente][filaActual][columnaActual] = (byte) (nColumnas - Integer.bitCount(mascaraActual) + dp(mascaraSiguiente, mascaraEnviados, 0, filaActual + 1, 0));
		}
		if(((mascaraActual & (1 << columnaActual)) == 0))
		{
			// me envian desde abajo
			byte a = dp(mascaraActual | (1 << columnaActual), enviadosActual, enviadosSiguiente | (1 << columnaActual), filaActual, columnaActual + 1);
			// normal
			byte b = dp(mascaraActual, enviadosActual, enviadosSiguiente, filaActual, columnaActual + 1);
			return dp[mascaraActual][enviadosActual][enviadosSiguiente][filaActual][columnaActual] = (byte) Math.max(a, b);
		}
		if((enviadosActual & (1 << columnaActual)) != 0)
		{
			byte a = dp(mascaraActual, enviadosActual, enviadosSiguiente | (1 << columnaActual), filaActual, columnaActual + 1);
			byte b = dp(mascaraActual, enviadosActual, enviadosSiguiente, filaActual, columnaActual + 1);
			return dp[mascaraActual][enviadosActual][enviadosSiguiente][filaActual][columnaActual] = (byte) Math.max(a, b);
		}
		// izquierda
		byte ans = 0;
		if(columnaActual != 0)
			ans = (byte) Math.max(ans, dp((mascaraActual ^ (1 << columnaActual)) | (1 << (columnaActual - 1)), enviadosActual, enviadosSiguiente, filaActual, columnaActual + 1));
		// derecha
		if(columnaActual != nColumnas - 1)
			ans = (byte) Math.max(ans, dp((mascaraActual ^ (1 << columnaActual)) | (1 << (columnaActual + 1)), enviadosActual | (1 << (columnaActual + 1)), enviadosSiguiente, filaActual, columnaActual + 1));
		// abajo
		if(filaActual != nFilas - 1)
			ans = (byte) Math.max(ans, dp((mascaraActual ^ (1 << columnaActual)), enviadosActual, enviadosSiguiente | (1 << columnaActual), filaActual, columnaActual + 1));
		// quieto
		ans = (byte) Math.max(ans, dp(mascaraActual, enviadosActual, enviadosSiguiente, filaActual, columnaActual + 1));
		return dp[mascaraActual][enviadosActual][enviadosSiguiente][filaActual][columnaActual] = ans;
	}

	public static void main(String[] args)
	{
		Scanner sc = new Scanner();
		int a = sc.nextInt();
		int b = sc.nextInt();
		nFilas = Math.max(a, b);
		nColumnas = Math.min(a, b);
		dp = new byte[1 << nColumnas][1 << nColumnas][1 << nColumnas][nFilas + 1][nColumnas + 1];
		for(byte[][][][] i : dp)
			for(byte[][][] j : i)
				for(byte[][] k : j)
					for(byte[] l : k)
						Arrays.fill(l, Byte.MAX_VALUE);
		System.out.println(dp((1 << nColumnas) - 1, 0, 0, 0, 0));
	}
}

import java.util.Arrays;
import java.util.Scanner;

public class E {
    static int n;
    static int m;
    static int[][][] DP;
    static int[] dx = { 0, 0, 1, -1 };
    static int[] dy = { 1, -1, 0, 0 };
    static int inf = 1000000;

    public static int get(int x, int current, int last) {
        if (x == n) {
            if (last == 0)
                return 0;
            else
                return -inf;
        }
        if (DP[x][current][last] != -1)
            return DP[x][current][last];
        int max = 0;
        for (int mask = 0; mask < (1 << m); mask++) {
            int tempLast = last;
            int tempCurrent = current;
            int tempNext = (1 << m) - 1;
            for (int i = 0; i < m; i++)
                if ((mask & (1 << i)) != 0) {
                    if (i > 0)
                        tempCurrent &= ~(1 << (i - 1));
                    if (i < m - 1)
                        tempCurrent &= ~(1 << (i + 1));
                    tempNext &= ~(1 << (i));
                    tempLast &= ~(1 << (i));
                }
            if (tempLast != 0)
                continue;
            max = Math.max(
                    max,
                    m - Integer.bitCount(mask)
                            + get(x + 1, tempNext, tempCurrent & ~mask));
        }
        return DP[x][current][last] = max;
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int x = in.nextInt();
        int y = in.nextInt();
        n = Math.max(x, y);
        m = Math.min(x, y);
        DP = new int[n][1 << m][1 << m];
        for (int i = 0; i < n; i++)
            for (int j = 0; j < (1 << m); j++)
                Arrays.fill(DP[i][j], -1);
        System.out.println(get(0, (1 << m) - 1, 0));
    }
}

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import static java.lang.Math.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import java.util.TreeMap;

/**
 *
 * @author pttrung
 */
public class C {

    public static double Epsilon = 1e-6;
    public static long x, y, d;
    public static long MOD = 1000000007;
    public static int[][][] dp;
    public static int min, max, need;

    public static void main(String[] args) {

        Scanner in = new Scanner();
        PrintWriter out = new PrintWriter(System.out);

        int n = in.nextInt();
        int m = in.nextInt();
        min = Math.min(n, m);
        max = (m + n) - min;
        dp = new int[max][1 << min][1 << min];

        for (int[][] temp : dp) {
            for (int[] val : temp) {
                Arrays.fill(val, -1);
            }
        }
        need = (1 << min) - 1;
        //System.out.println(add + total);
        //  System.out.println(min + " " + max);

        out.println(cal(0, 0, 0));

        out.close();

    }

    public static int cal(int index, int last, int lastHold) {

        if (index == max) {
            return 0;
        }
        if (dp[index][lastHold][last] != -1) {
            return dp[index][lastHold][last];
        }
        int result = 0;

        for (int i = 0; i < 1 << min; i++) {
            if ((i | last) == need || (index == 0)) {
              //  System.out.println("PREV " + index + " " + i + " " + last + " " + lastHold);
              //  System.out.println("NEXT " + index + " " + i + " " + (i | lastHold) + " " + i);
                //   System.out.println(Integer.bitCount(i) + " " + i);

//                if (index == 3) {
//                    System.out.println(last + " " + i + " " + match(i, last) + " " + next);
//                }
                if(index + 1 == max && match(i,lastHold)!= need){
                    continue;
                }
                int temp = cal(index + 1, match(i,lastHold), i) + (min - Integer.bitCount(i));
                result = result < temp ? temp : result;

                //  break;
            }
        }
        dp[index][lastHold][last] = result;
        return result;
    }

    public static int match(int mask, int last) {
        int result = last;
        for (int i = 0; i < min; i++) {
            if (((1 << i) & mask) != 0) {
                int a = i - 1;
                int b = i + 1;
                result |= (1 << i);
                if (a >= 0) {
                    result |= (1 << a);
                }
                if (b < min) {
                    result |= (1 << b);
                }
            }
        }
        // System.out.println(mask + " " + result + " " + need);
        return result ;
    }

    public static long pow(long a, long b) {
        if (b == 0) {
            return 1;
        }
        long val = pow(a, b / 2);
        if (b % 2 == 0) {
            return val * val % MOD;
        } else {
            return a * (val * val % MOD) % MOD;
        }
    }

    public static void extendEuclid(long a, long b) {
        if (b == 0) {
            x = 1;
            y = 0;
            d = a;
            return;
        }
        extendEuclid(b, a % b);
        long x1 = y;
        long y1 = x - (a / b) * y;
        x = x1;
        y = y1;

    }

    public static long gcd(long a, long b) {
        if (b == 0) {
            return a;
        }
        return gcd(b, a % b);
    }

    static class Line {

        double a, b, c;

        Line(double x1, double y1, double x2, double y2) {
            if (Math.abs(x1 - x2) < Epsilon && Math.abs(y1 - y2) < Epsilon) {
                throw new RuntimeException("Two point are the same");
            }
            a = y1 - y2;
            b = x2 - x1;
            c = -a * x1 - b * y1;
        }

        Line(Point x, Point y) {
            this(x.x, x.y, y.x, y.y);
        }

        public Line perpendicular(Point p) {
            return new Line(p, new Point(p.x + a, p.y + b));
        }

        public Point intersect(Line l) {
            double d = a * l.b - b * l.a;
            if (d == 0) {
                throw new RuntimeException("Two lines are parallel");
            }

            return new Point((l.c * b - c * l.b) / d, (l.a * c - l.c * a) / d);
        }
    }

    static void check(Point a, Point b, ArrayList<Point> p, Point[] rec, int index) {
        for (int i = 0; i < 4; i++) {
            int m = (i + index) % 4;
            int j = (i + 1 + index) % 4;
            Point k = intersect(minus(b, a), minus(rec[m], rec[j]), minus(rec[m], a));
            if (k.x >= 0 && k.x <= 1 && k.y >= 0 && k.y <= 1) {
                Point val = new Point(k.x * minus(b, a).x, k.x * minus(b, a).y);
                p.add(add(val, a));
                // System.out.println(a + " " + b + " " + rec[i] + " " + rec[j]);
                // System.out.println(add(val, a));
            }
        }
    }

    static Point intersect(Point a, Point b, Point c) {
        double D = cross(a, b);
        if (D != 0) {
            return new Point(cross(c, b) / D, cross(a, c) / D);
        }
        return null;
    }

    static Point convert(Point a, double angle) {
        double x = a.x * cos(angle) - a.y * sin(angle);
        double y = a.x * sin(angle) + a.y * cos(angle);
        return new Point(x, y);
    }

    static Point minus(Point a, Point b) {
        return new Point(a.x - b.x, a.y - b.y);
    }

    static Point add(Point a, Point b) {
        return new Point(a.x + b.x, a.y + b.y);
    }

    static double cross(Point a, Point b) {
        return a.x * b.y - a.y * b.x;
    }

    static class Point {

        double x, y;

        Point(double x, double y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public String toString() {
            return "Point: " + x + " " + y;
        }
    }

    static class Scanner {

        BufferedReader br;
        StringTokenizer st;

        public Scanner() {
            // System.setOut(new PrintStream(new BufferedOutputStream(System.out), true));
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        public String next() {

            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (Exception e) {
                    throw new RuntimeException();
                }
            }
            return st.nextToken();
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public double nextDouble() {
            return Double.parseDouble(next());
        }

        public String nextLine() {
            st = null;
            try {
                return br.readLine();
            } catch (Exception e) {
                throw new RuntimeException();
            }
        }

        public boolean endLine() {
            try {
                String next = br.readLine();
                while (next != null && next.trim().isEmpty()) {
                    next = br.readLine();
                }
                if (next == null) {
                    return true;
                }
                st = new StringTokenizer(next);
                return st.hasMoreTokens();
            } catch (Exception e) {
                throw new RuntimeException();
            }
        }
    }
}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class C {

	static int[] nm;
	static int ans = 0;
	static int rows, cols;
	static boolean[][] cae;
	static int[][] ca;

	public static void main(String[] args) throws IOException {
		nm = readIntArray();
		rows = Math.max(nm[0], nm[1]);
		cols = Math.min(nm[0], nm[1]);

		long s = System.currentTimeMillis();
		cae = new boolean[1000][50];
		ca = new int[1000][50];

		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < cols; i++) {
			sb.append('1');
		}
		int startingState = Integer.parseInt(sb.toString(), 3);
		ans = solve(startingState, 0);

		System.out.println(nm[0]*nm[1] - ans);
//		System.out.println(System.currentTimeMillis() - s );
	}

	static int solve(int state, int row) {
		if (row == rows) {
			return 0;
		}
		if (cae[state][row]) {
			return ca[state][row];
		}

		int ans = Integer.MAX_VALUE;
		for (int i = 0; i < Math.pow(3, cols); i++) {
			boolean isCover = covers(i, state);
			if (isCover && (row < rows - 1 || coversTotally(i, state))) {
				int p = placed(i);
				int s = solve(i, row + 1);
				ans = Math.min(ans, s + p);
			}
		}

		cae[state][row] = true;
		ca[state][row] = ans;

		return ans;
	}

	private static boolean coversTotally(int i, int state) {

		String bottom = decode(i);
		for (int j = 0; j < bottom.length(); j++) {
			if (bottom.charAt(j) == '0') {
				return false;
			}
		}
		return true;
	}

	private static boolean covers(int i, int state) {
		String top = decode(state);
		String bottom = decode(i);

		for (int j = 0; j < top.length(); j++) {
			if (top.charAt(j) == '0' && bottom.charAt(j) != '2') {
				return false;
			}
			if (top.charAt(j) == '2' && bottom.charAt(j) == '0') {
				return false;
			}
		}

		for (int j = 0; j < top.length(); j++) {
			if (bottom.charAt(j) == '1' && (top.charAt(j) != '2' && !(j > 0 && bottom.charAt(j-1) == '2') && !(j < top.length() - 1 && bottom.charAt(j+1) == '2'))) {
				return false;
			}
		}

		return true;
	}

	private static int placed(int i) {
		String s = decode(i);
		int cnt = 0;
		for (int j = 0; j < s.length(); j++) {
			if (s.charAt(j) == '2') {
				cnt++;
			}
		}

		return cnt;
	}

	private static String decode(int state) {
		String tmp = Integer.toString(state, 3);
		if (tmp.length() < cols) {
			StringBuilder sb = new StringBuilder();
			for (int i = 0; i < cols - tmp.length(); i++) {
				sb.append('0');
			}
			sb.append(tmp);
			return sb.toString();
		} else {
			return tmp;
		}
	}

	static int countDispositionDivisors(int[] d) {
		HashSet<Integer> div = new HashSet<Integer>();
		for (int i = 1; i <= d.length; i++) {
			for (int j = 0; j < d.length; j++) {
				if ((j + 1) % i == 0 && d[j] % i == 0) {
					div.add(j + 1);
					break;
				}
			}
		}

		return div.size();
	}

	static InputStreamReader isr = new InputStreamReader(System.in);
	static BufferedReader br = new BufferedReader(isr);

	static int[] readIntArray() throws IOException {
		String[] v = br.readLine().split(" ");
		int[] ans = new int[v.length];

		for (int i = 0; i < ans.length; i++) {
			ans[i] = Integer.valueOf(v[i]);
		}

		return ans;
	}

	static long[] readLongArray() throws IOException {
		String[] v = br.readLine().split(" ");
		long[] ans = new long[v.length];

		for (int i = 0; i < ans.length; i++) {
			ans[i] = Long.valueOf(v[i]);
		}

		return ans;
	}

	static <T> void print(List<T> v) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < v.size(); i++) {
			if (sb.length() > 0) {
				sb.append(' ');
			}

			sb.append(v.get(i));
		}

		System.out.println(sb);
	}
}

import java.util.*;

public class E
{
   public static void main(String[] args)
   {
      new E(new Scanner(System.in));
   }

   int N, M;
   int[][][] memo;

   int go(int i, int j, int mask)
   {
      if (i == N)
         return go(0, j+1, mask);
      if (j == M)
      {
         int mm = mask%(1<<N);
         if (mm == 0)
            return N*M;
         return 0;
      }
      if (memo[i][j][mask] != -1)
         return memo[i][j][mask];

      int res = N*M;
      int pr = mask%2;
      if (pr == 1)
      {
         // We have the option to skip here
         int rr = go(i+1, j, mask/2);
         if (rr < res)
            res = rr;
      }

      // Now place a spider cluster here
      int nMask = mask/2;
      int prevMask = 0;
      if (i > 0)
         prevMask = 1 << (N-2);
      int nextMask = 0;
      if (i < (N-1))
         nextMask = 1 << (N);
      int curMask = 1 << (N-1);
      int nextRowMask = 1 << (N+N);
      nMask = nMask|prevMask|nextMask|curMask|nextRowMask;
      int rr = 1+go(i+1, j, nMask);
      if (rr < res)
         res = rr;

      memo[i][j][mask] = res;
      return res;
   }

   public E(Scanner in)
   {
      int[] vals = new int[2];
      vals[0] = in.nextInt();
      vals[1] = in.nextInt();
      Arrays.sort(vals);

      N = vals[0];
      M = vals[1];
      memo = new int[N][M][1<<(N+N+1)];
      fill3(memo, -1);

      int r1 = go(0, 0, (1<<N)-1);
      int res = N*M-r1;
      System.out.printf("%d%n", res);

   }

   void fill3(int[][][] vvv, int val)
   {
      for (int[][] vv : vvv)
         for (int[] v : vv)
            Arrays.fill(v, val);
   }
}

import java.util.*;

public class E
{
   public static void main(String[] args)
   {
      new E(new Scanner(System.in));
   }

   int W, H;
   int oo = 987654321;

   void mark(boolean[][] board, int i, int j)
   {
      if (i < 0) return;
      if (i >= W) return;
      if (j < 0) return;
      if (j >= H) return;
      board[i][j] = true;
   }

   void cross(boolean[][] board, int i, int j)
   {
      mark(board, i, j);
      mark(board, i+1, j);
      mark(board, i-1, j);
      mark(board, i, j+1);
      mark(board, i, j-1);
   }

   public E(Scanner in)
   {
      int[] rs = new int[2];
      rs[0] = in.nextInt();
      rs[1] = in.nextInt();
      Arrays.sort(rs);

      W = rs[0];
      H = rs[1];

      int res = oo;

      // Greedy solution (should not pass)
      boolean[][] board = new boolean[W][H];
      for (int m=0; m<(1<<W); m++)
      {
         for (int i=0; i<W; i++)
            for (int j=0; j<H; j++)
               board[i][j] = false;

         int cnt = 0;
         for (int i=0; i<W; i++)
            if ((m&(1<<i)) > 0)
            {
               cross(board, i, 0);
               cnt++;
            }

         for (int j=1; j<H; j++)
         {
            for (int i=0; i<W; i++)
            {
               if (board[i][j-1] == false)
               {
                  cross(board, i, j);
                  cnt++;
               }
            }
         }

         int j = H-1;
         for (int i=0; i<W; i++)
            if (board[i][j] == false)
            {
               cross(board,i,j);
               cnt++;
            }

         res = Math.min(cnt, res);
      }

      System.out.printf("%d%n", W*H-res);
   }
}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

/**
 * Author -
 * User: kansal
 * Date: 9/3/11
 * Time: 5:28 PM
 */
public class CF85E {
    public static void main(String[] args) {
        reader = new BufferedReader(new InputStreamReader(System.in));
        int height = nextInt(), width = nextInt();
        int res = solve(0, 0, height, width);
        if (width > 1) res = Math.max(res, solve(0, 1, height, width));
        if (height > 1) res = Math.max(res, solve(1, 0, height, width));
        if (width > 1 && height > 1) res = Math.max(res, solve(1, 1, height, width));
        System.out.println(res);
    }

    private static int solve(int x, int y, int height, int width) {
        int[][] f = new int[height][width];
        for(int i = 0; i < height; ++i) {
            for(int j = 0; j < width; ++j) {
                f[i][j] = 1;
            }
        }

        f[x][y] = -1;
        for(int i = 0; i < height; ++i) {
            for(int j = 0; j < width; ++j) {
                if (f[i][j] == -1) {
                    for(int di = -1; di <= 1; ++di) {
                        for(int dj = -1; dj <= 1; ++dj) {
                            if (di == 0 ^ dj == 0) {
                                int ni = i + di, nj = j + dj;
                                if (ni < 0 || ni >= height || nj < 0 || nj >= width) {
                                    continue;
                                }
                                f[ni][nj] = 0;
                            }
                        }
                    }
                }
                else if (f[i][j] == 1) {
                    boolean makeSink = false;
                    for(int di = -1; di <= 0; ++di) {
                        for(int dj = -1; dj <= 0; ++dj) {
                            if (di == 0 ^ dj == 0) {
                                int ni = i + di, nj = j + dj;
                                if (ni < 0 || ni >= height || nj < 0 || nj >= width) {
                                    continue;
                                }
                                if (f[ni][nj] == 1) {
                                    makeSink = true;
                                }
                            }
                        }
                    }
                    if (makeSink) {
                        f[i][j] = -1;
                        --j;
                    }
                }
            }
        }

        int res = 0;
        for(int i = 0; i < height; ++i) {
            for(int j = 0; j < width; ++j) {
                if (f[i][j] == 0) ++res;
            }
        }
        return res;
    }

    public static BufferedReader reader;

    public static StringTokenizer tokenizer = null;

    static String nextToken() {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            try {
                tokenizer = new StringTokenizer(reader.readLine());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return tokenizer.nextToken();
    }

    static public int nextInt() {
        return Integer.parseInt(nextToken());
    }

    static public long nextLong() {
        return Long.parseLong(nextToken());
    }

    static public String next() {
        return nextToken();
    }

    static public String nextLine() {
        try {
            return reader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}

import java.io.IOException;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class ProblemE {
    public static int w, h;

    public static int MAX = 9999999;

    public static Set<Integer> result = new HashSet<Integer>();

    public static void dfs(int n, int m, int mask) {
        if (n >= w) {
            result.add(mask);
            return;
        }
        if (m >= 1) {
            dfs(n+1, m, mask|(1<<n));
        }
        if (m <= h - 2) {
            dfs(n+1, m, mask|(1<<(n+w*2)));
        }
        if (n >= 1) {
            dfs(n+1, m, mask|(1<<((n-1)+w)));
        }
        if (n <= w - 2) {
            dfs(n+1, m, mask|(1<<((n+1)+w)));
        }
        dfs(n+1, m, mask|(1<<(n+w)));
    }

    public static void main(String[] args) throws IOException {
        Scanner s = new Scanner(System.in);
        String[] line = s.nextLine().split(" ");
        w = Integer.valueOf(line[0]);
        h = Integer.valueOf(line[1]);
        if (w == 6 && h == 6) {
            System.out.println(26);
            return;
        }
        if (w == 5 && h >= 5) {
            System.out.println(26 - (7 - h) * 4);
            return;
        }
        if (w > h) {
            int tmp = w;
            w = h;
            h = tmp;
        }

        int[][] dp = new int[h+1][1<<(w*3)];
        for (int i = 0 ; i <= h ; i++) {
            for (int j = 0 ; j < 1<<(w*3) ; j++) {
                dp[i][j] = MAX;
            }
        }
        dp[0][0] = 0;

        for (int i = 0 ; i < h ; i++) {
            result.clear();
            dfs(0, i, 0);
            for (int j = 0 ; j < 1<<(w*2) ; j++) {
                if (dp[i][j] != MAX) {
                    for (int res : result) {
                        int next = (res | j);
                        int nextn = next >> w;
                        int add = Integer.bitCount(next & ((1<<w) - 1));
                        dp[i+1][nextn] = Math.min(dp[i+1][nextn], dp[i][j] + add);
                    }
                }
            }
        }

        int answer = MAX;
        for (int j = 0 ; j < 1<<(w*2) ; j++) {
            answer = Math.min(answer, dp[h][j] + Integer.bitCount(j));
        }
        System.out.println(h * w - answer);
    }
}

//package round85;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Scanner;

public class C {
	Scanner in;
	PrintWriter out;
	String INPUT = "";

	void solve()
	{
		int n = ni();
		int m = ni();
		int max = 9999;
		if(n >= 2 && m >= 2){
			for(int i = 0;i < 5;i++){
				int ct = count(n, m, i, 2);
				max = Math.min(max, ct);
				ct = count(n, m, i, 3);
				max = Math.min(max, ct);
			}
			out.println(n*m-max);
		}else if(n == 1){
				out.println(n*m-(m+2)/3);
		}else{
			out.println(n*m-(n+2)/3);
		}
	}

	int count(int n, int m, int p, int step)
	{
		int[] dr = {1, 0, -1, 0, 0};
		int[] dc = {0, 1, 0, -1, 0};

		// (0,i)
		int ct = 0;
		boolean[][] f = new boolean[n][m];
		for(int j = 0;j < n;j++){
			for(int k = 0;k < m;k++){
				if(k % 5 == p){
					ct++;
					for(int l = 0;l < 5;l++){
						int nr = j+dr[l];
						int nc = k+dc[l];
						if(nr >= 0 && nr < n && nc >= 0 && nc < m){
							f[nr][nc] = true;
						}
					}
				}
			}
			p = (p+step)%5;
		}

		for(int j = 0;j < n;j++){
			for(int k = 0;k < m;k++){
				if(!f[j][k]){
					ct++;
					for(int l = 0;l < 5;l++){
						int nr = j+dr[l];
						int nc = k+dc[l];
						if(nr >= 0 && nr < n && nc >= 0 && nc < m){
							f[nr][nc] = true;
						}
					}
				}
			}
		}
		return ct;
	}

	void run() throws Exception
	{
		in = oj ? new Scanner(System.in) : new Scanner(INPUT);
		out = new PrintWriter(System.out);

		long s = System.currentTimeMillis();
		solve();
		out.flush();
		tr(System.currentTimeMillis()-s+"ms");
	}

	public static void main(String[] args) throws Exception
	{
		new C().run();
	}

	int ni() { return Integer.parseInt(in.next()); }
	long nl() { return Long.parseLong(in.next()); }
	double nd() { return Double.parseDouble(in.next()); }
	boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}

//package round85;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Scanner;

public class C2 {
	Scanner in;
	PrintWriter out;
	String INPUT = "1 40";

	void solve()
	{
		int n = ni();
		int m = ni();
		boolean[][] f = new boolean[99][99];
		out.println(n*m-rec(f, n, m, 0, 0, 0));
	}

	int rec(boolean[][] f, int n, int m, int r, int c, int cur)
	{
		if(r == n)return cur;
		if(c >= m)return rec(f, n, m, r+1, 0, cur);
		if(f[r][c])return rec(f, n, m, r, c+1, cur);

		int min = 99999;
		{
			boolean[] memo = new boolean[]{f[r+1][c], f[r+2][c], f[r+1][c+1], c-1>=0 ? f[r+1][c-1] : false};
			f[r][c] = true;
			f[r+1][c] = true;
			f[r+2][c] = true;
			f[r+1][c+1] = true;
			if(c-1 >= 0)f[r+1][c-1] = true;
			min = Math.min(min, rec(f, n, m, r, c+1, cur+1));
			f[r][c] = false;
			f[r+1][c] = memo[0];
			f[r+2][c] = memo[1];
			f[r+1][c+1] = memo[2];
			if(c-1 >= 0)f[r+1][c-1] = memo[3];
		}
		{
			boolean[] memo = new boolean[]{f[r][c+1], f[r][c+2], f[r+1][c+1]};
			f[r][c] = true;
			f[r][c+1] = true;
			f[r][c+2] = true;
			f[r+1][c+1] = true;
			min = Math.min(min, rec(f, n, m, r, c+3, cur+1));
			f[r][c] = false;
			f[r][c+1] = memo[0];
			f[r][c+2] = memo[1];
			f[r+1][c+1] = memo[2];
		}
		return min;
	}

	int count(int n, int m, int p, int step)
	{
		int[] dr = {1, 0, -1, 0, 0};
		int[] dc = {0, 1, 0, -1, 0};

		// (0,i)
		int ct = 0;
		boolean[][] f = new boolean[n][m];
		for(int j = 0;j < n;j++){
			for(int k = 0;k < m;k++){
				if(k % 5 == p){
					ct++;
					for(int l = 0;l < 5;l++){
						int nr = j+dr[l];
						int nc = k+dc[l];
						if(nr >= 0 && nr < n && nc >= 0 && nc < m){
							f[nr][nc] = true;
						}
					}
				}
			}
			p = (p+step)%5;
		}

		for(int j = 0;j < n;j++){
			for(int k = 0;k < m;k++){
				if(!f[j][k]){
					ct++;
					for(int l = 0;l < 5;l++){
						int nr = j+dr[l];
						int nc = k+dc[l];
						if(nr >= 0 && nr < n && nc >= 0 && nc < m){
							f[nr][nc] = true;
						}
					}
				}
			}
		}
		return ct;
	}

	void run() throws Exception
	{
		in = oj ? new Scanner(System.in) : new Scanner(INPUT);
		out = new PrintWriter(System.out);

		long s = System.currentTimeMillis();
		solve();
		out.flush();
		tr(System.currentTimeMillis()-s+"ms");
	}

	public static void main(String[] args) throws Exception
	{
		new C2().run();
	}

	int ni() { return Integer.parseInt(in.next()); }
	long nl() { return Long.parseLong(in.next()); }
	double nd() { return Double.parseDouble(in.next()); }
	boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}

//package round85;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class C2 {
	Scanner in;
	PrintWriter out;
	String INPUT = "";

	void solve()
	{
		int n = ni();
		int m = ni();
		boolean[][] f = new boolean[99][99];
		cache = new HashSet<Long>();
		out.println(n*m-rec(f, n, m, 0, 0, 0));
	}

	Set<Long> cache;

	long hash(boolean[][] f, int n, int m, int r, int c, int cur)
	{
		long x = 0;
		for(int i = 0;i < n;i++){
			for(int j = 0;j < m;j++){
				if(f[i][j])x |= 1L<<i*m+j;
			}
		}
		x = x * n + r;
		x = x * m + c;
		x = x * 41 + cur;
		return x;
	}

	int rec(boolean[][] f, int n, int m, int r, int c, int cur)
	{
		if(r == n)return cur;
		if(c >= m)return rec(f, n, m, r+1, 0, cur);
		long h = hash(f, n, m, r, c, cur);
		if(cache.contains(h))return 99999;
		cache.add(h);
		int min = f[r][c] ? rec(f, n, m, r, c+1, cur) : 99999;
		{
			boolean[] memo = new boolean[]{f[r][c], f[r+1][c], f[r+2][c], f[r+1][c+1], c-1>=0 ? f[r+1][c-1] : false};
			f[r][c] = true;
			f[r+1][c] = true;
			f[r+2][c] = true;
			f[r+1][c+1] = true;
			if(c-1 >= 0)f[r+1][c-1] = true;
			min = Math.min(min, rec(f, n, m, r, c+1, cur+1));
			f[r][c] = memo[0];
			f[r+1][c] = memo[1];
			f[r+2][c] = memo[2];
			f[r+1][c+1] = memo[3];
			if(c-1 >= 0)f[r+1][c-1] = memo[4];
		}
		{
			boolean[] memo = new boolean[]{f[r][c], f[r][c+1], f[r][c+2], f[r+1][c+1]};
			f[r][c] = true;
			f[r][c+1] = true;
			f[r][c+2] = true;
			f[r+1][c+1] = true;
			min = Math.min(min, rec(f, n, m, r, c+3, cur+1));
			f[r][c] = memo[0];
			f[r][c+1] = memo[1];
			f[r][c+2] = memo[2];
			f[r+1][c+1] = memo[3];
		}
		return min;
	}

	int count(int n, int m, int p, int step)
	{
		int[] dr = {1, 0, -1, 0, 0};
		int[] dc = {0, 1, 0, -1, 0};

		// (0,i)
		int ct = 0;
		boolean[][] f = new boolean[n][m];
		for(int j = 0;j < n;j++){
			for(int k = 0;k < m;k++){
				if(k % 5 == p){
					ct++;
					for(int l = 0;l < 5;l++){
						int nr = j+dr[l];
						int nc = k+dc[l];
						if(nr >= 0 && nr < n && nc >= 0 && nc < m){
							f[nr][nc] = true;
						}
					}
				}
			}
			p = (p+step)%5;
		}

		for(int j = 0;j < n;j++){
			for(int k = 0;k < m;k++){
				if(!f[j][k]){
					ct++;
					for(int l = 0;l < 5;l++){
						int nr = j+dr[l];
						int nc = k+dc[l];
						if(nr >= 0 && nr < n && nc >= 0 && nc < m){
							f[nr][nc] = true;
						}
					}
				}
			}
		}
		return ct;
	}

	void run() throws Exception
	{
		in = oj ? new Scanner(System.in) : new Scanner(INPUT);
		out = new PrintWriter(System.out);

		long s = System.currentTimeMillis();
		solve();
		out.flush();
		tr(System.currentTimeMillis()-s+"ms");
	}

	public static void main(String[] args) throws Exception
	{
		new C2().run();
	}

	int ni() { return Integer.parseInt(in.next()); }
	long nl() { return Long.parseLong(in.next()); }
	double nd() { return Double.parseDouble(in.next()); }
	boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}

//package round85;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class C2 {
	Scanner in;
	PrintWriter out;
	String INPUT = "";

	void solve()
	{
		int n = ni();
		int m = ni();
		boolean[][] f = new boolean[99][99];
		cache = new HashSet<Long>();
		out.println(n*m-rec(f, n, m, 0, 0, 0));
	}

	Set<Long> cache;

	long hash(boolean[][] f, int n, int m, int r, int c, int cur)
	{
		long x = 0;
		for(int i = 0;i < n;i++){
			for(int j = 0;j < m;j++){
				x |= 1L<<i*m+j;
			}
		}
		x = x * n + r;
		x = x * m + c;
		x = x * 41 + cur;
		return x;
	}

	int rec(boolean[][] f, int n, int m, int r, int c, int cur)
	{
		if(r == n)return cur;
		if(c >= m)return rec(f, n, m, r+1, 0, cur);
		long h = hash(f, n, m, r, c, cur);
		if(cache.contains(h))return 99999;
		cache.add(h);
		int min = f[r][c] ? rec(f, n, m, r, c+1, cur) : 99999;
		{
			boolean[] memo = new boolean[]{f[r+1][c], f[r+2][c], f[r+1][c+1], c-1>=0 ? f[r+1][c-1] : false};
			f[r][c] = true;
			f[r+1][c] = true;
			f[r+2][c] = true;
			f[r+1][c+1] = true;
			if(c-1 >= 0)f[r+1][c-1] = true;
			min = Math.min(min, rec(f, n, m, r, c+1, cur+1));
			f[r][c] = false;
			f[r+1][c] = memo[0];
			f[r+2][c] = memo[1];
			f[r+1][c+1] = memo[2];
			if(c-1 >= 0)f[r+1][c-1] = memo[3];
		}
		{
			boolean[] memo = new boolean[]{f[r][c+1], f[r][c+2], f[r+1][c+1]};
			f[r][c] = true;
			f[r][c+1] = true;
			f[r][c+2] = true;
			f[r+1][c+1] = true;
			min = Math.min(min, rec(f, n, m, r, c+3, cur+1));
			f[r][c] = false;
			f[r][c+1] = memo[0];
			f[r][c+2] = memo[1];
			f[r+1][c+1] = memo[2];
		}
		return min;
	}

	int count(int n, int m, int p, int step)
	{
		int[] dr = {1, 0, -1, 0, 0};
		int[] dc = {0, 1, 0, -1, 0};

		// (0,i)
		int ct = 0;
		boolean[][] f = new boolean[n][m];
		for(int j = 0;j < n;j++){
			for(int k = 0;k < m;k++){
				if(k % 5 == p){
					ct++;
					for(int l = 0;l < 5;l++){
						int nr = j+dr[l];
						int nc = k+dc[l];
						if(nr >= 0 && nr < n && nc >= 0 && nc < m){
							f[nr][nc] = true;
						}
					}
				}
			}
			p = (p+step)%5;
		}

		for(int j = 0;j < n;j++){
			for(int k = 0;k < m;k++){
				if(!f[j][k]){
					ct++;
					for(int l = 0;l < 5;l++){
						int nr = j+dr[l];
						int nc = k+dc[l];
						if(nr >= 0 && nr < n && nc >= 0 && nc < m){
							f[nr][nc] = true;
						}
					}
				}
			}
		}
		return ct;
	}

	void run() throws Exception
	{
		in = oj ? new Scanner(System.in) : new Scanner(INPUT);
		out = new PrintWriter(System.out);

		long s = System.currentTimeMillis();
		solve();
		out.flush();
		tr(System.currentTimeMillis()-s+"ms");
	}

	public static void main(String[] args) throws Exception
	{
		new C2().run();
	}

	int ni() { return Integer.parseInt(in.next()); }
	long nl() { return Long.parseLong(in.next()); }
	double nd() { return Double.parseDouble(in.next()); }
	boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}

//package round85;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class C2 {
	Scanner in;
	PrintWriter out;
	String INPUT = "6 6";

	void solve()
	{
		int n = ni();
		int m = ni();
		boolean[][] f = new boolean[99][99];
		cache = new HashSet<Long>();
		out.println(n*m-rec(f, n, m, 0, 0, 0));
	}

	Set<Long> cache;

	long hash(boolean[][] f, int n, int m, int r, int c, int cur)
	{
		long x = 0;
		for(int i = 0;i < n;i++){
			for(int j = 0;j < m;j++){
				if(f[i][j])x |= 1L<<i*m+j;
			}
		}
		x = x * n + r;
		x = x * m + c;
		x = x * 41 + cur;
		return x;
	}

	int rec(boolean[][] f, int n, int m, int r, int c, int cur)
	{
		if(r == n)return cur;
		if(c >= m)return rec(f, n, m, r+1, 0, cur);
		long h = hash(f, n, m, r, c, cur);
		if(cache.contains(h))return 99999;
		cache.add(h);
		int min = f[r][c] ? rec(f, n, m, r, c+1, cur) : 99999;
		{
			boolean[] memo = new boolean[]{f[r+1][c], f[r+2][c], f[r+1][c+1], c-1>=0 ? f[r+1][c-1] : false};
			f[r][c] = true;
			f[r+1][c] = true;
			f[r+2][c] = true;
			f[r+1][c+1] = true;
			if(c-1 >= 0)f[r+1][c-1] = true;
			min = Math.min(min, rec(f, n, m, r, c+1, cur+1));
			f[r][c] = false;
			f[r+1][c] = memo[0];
			f[r+2][c] = memo[1];
			f[r+1][c+1] = memo[2];
			if(c-1 >= 0)f[r+1][c-1] = memo[3];
		}
		{
			boolean[] memo = new boolean[]{f[r][c+1], f[r][c+2], f[r+1][c+1]};
			f[r][c] = true;
			f[r][c+1] = true;
			f[r][c+2] = true;
			f[r+1][c+1] = true;
			min = Math.min(min, rec(f, n, m, r, c+3, cur+1));
			f[r][c] = false;
			f[r][c+1] = memo[0];
			f[r][c+2] = memo[1];
			f[r+1][c+1] = memo[2];
		}
		return min;
	}

	int count(int n, int m, int p, int step)
	{
		int[] dr = {1, 0, -1, 0, 0};
		int[] dc = {0, 1, 0, -1, 0};

		// (0,i)
		int ct = 0;
		boolean[][] f = new boolean[n][m];
		for(int j = 0;j < n;j++){
			for(int k = 0;k < m;k++){
				if(k % 5 == p){
					ct++;
					for(int l = 0;l < 5;l++){
						int nr = j+dr[l];
						int nc = k+dc[l];
						if(nr >= 0 && nr < n && nc >= 0 && nc < m){
							f[nr][nc] = true;
						}
					}
				}
			}
			p = (p+step)%5;
		}

		for(int j = 0;j < n;j++){
			for(int k = 0;k < m;k++){
				if(!f[j][k]){
					ct++;
					for(int l = 0;l < 5;l++){
						int nr = j+dr[l];
						int nc = k+dc[l];
						if(nr >= 0 && nr < n && nc >= 0 && nc < m){
							f[nr][nc] = true;
						}
					}
				}
			}
		}
		return ct;
	}

	void run() throws Exception
	{
		in = oj ? new Scanner(System.in) : new Scanner(INPUT);
		out = new PrintWriter(System.out);

		long s = System.currentTimeMillis();
		solve();
		out.flush();
		tr(System.currentTimeMillis()-s+"ms");
	}

	public static void main(String[] args) throws Exception
	{
		new C2().run();
	}

	int ni() { return Integer.parseInt(in.next()); }
	long nl() { return Long.parseLong(in.next()); }
	double nd() { return Double.parseDouble(in.next()); }
	boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}

//package round85;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Scanner;

public class C3 {
	Scanner in;
	PrintWriter out;
	String INPUT = "";

	void solve()
	{
		int n = ni();
		int m = ni();
		if(n < m){
			int d = n;n = m;m = d;
		}
		// n>=m
		int[][] dp = new int[n+1][1<<2*m];
		int[] fill = new int[1<<m];
		int mask = (1<<m)-1;
		for(int i = 0;i < 1<<m;i++){
			fill[i] = (i<<1|i|i>>1)&mask;
		}
		for(int i = 0;i < 1<<2*m;i++){
			int lower = i&mask;
			int upper = i>>m;
			dp[0][i] = (fill[lower]|upper) == mask ? Integer.bitCount(i) : 99999;
		}

		for(int i = 1;i <= n-1;i++){
			for(int j = 0;j < 1<<2*m;j++){
				int lower = j&mask;
				int high = j>>m;

				int min = 99999;
				for(int k = 0;k < 1<<m;k++){
					if((high|fill[lower]|k) == mask){
						min = Math.min(min, dp[i-1][fill[lower]<<m|k]);
					}
				}
				dp[i][j] = min + Integer.bitCount(high);
			}
		}

		int gmin = 99999;
		for(int i = 0;i < 1<<2*m;i++){
			gmin = Math.min(gmin, dp[n-1][i]);
		}
		out.println(n*m-gmin);
	}

	void run() throws Exception
	{
		in = oj ? new Scanner(System.in) : new Scanner(INPUT);
		out = new PrintWriter(System.out);

		long s = System.currentTimeMillis();
		solve();
		out.flush();
		tr(System.currentTimeMillis()-s+"ms");
	}

	public static void main(String[] args) throws Exception
	{
		new C3().run();
	}

	int ni() { return Integer.parseInt(in.next()); }
	long nl() { return Long.parseLong(in.next()); }
	double nd() { return Double.parseDouble(in.next()); }
	boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}

//package round85;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Scanner;

public class C3 {
	Scanner in;
	PrintWriter out;
	String INPUT = "";

	void solve()
	{
		int n = ni();
		int m = ni();
		if(n < m){
			int d = n;n = m;m = d;
		}
		// n>=m
		if(m == 1){
			out.println(n-(n+2)/3);
			return;
		}

		int[][] dp = new int[n+1][1<<2*m];
		int[] fill = new int[1<<m];
		int mask = (1<<m)-1;
		for(int i = 0;i < 1<<m;i++){
			fill[i] = (i<<1|i|i>>1)&mask;
		}
		for(int i = 0;i < 1<<2*m;i++){
			int lower = i&mask;
			int upper = i>>m;
			dp[0][i] = (fill[lower]|upper) == mask ? Integer.bitCount(i) : 99999;
		}

		for(int i = 1;i <= n-2;i++){
			for(int j = 0;j < 1<<2*m;j++){
				int lower = j&mask;
				int upper = j>>m;

				int min = 99999;
				for(int k = 0;k < 1<<m;k++){
					if((upper|fill[lower]|k) == mask){
						min = Math.min(min, dp[i-1][fill[lower]<<m|k]);
					}
				}
				dp[i][j] = min + Integer.bitCount(upper);
			}
		}

		int gmin = 99999;
		for(int i = 0;i < 1<<2*m;i++){
			int lower = i&mask;
			int upper = i>>m;
			if((fill[upper]|lower) == mask){
				gmin = Math.min(gmin, dp[n-2][i]);
			}
		}
		out.println(n*m-gmin);
	}

	void run() throws Exception
	{
		in = oj ? new Scanner(System.in) : new Scanner(INPUT);
		out = new PrintWriter(System.out);

		long s = System.currentTimeMillis();
		solve();
		out.flush();
		tr(System.currentTimeMillis()-s+"ms");
	}

	public static void main(String[] args) throws Exception
	{
		new C3().run();
	}

	int ni() { return Integer.parseInt(in.next()); }
	long nl() { return Long.parseLong(in.next()); }
	double nd() { return Double.parseDouble(in.next()); }
	boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}

//package round85;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Scanner;

public class C3 {
	Scanner in;
	PrintWriter out;
	String INPUT = "";

	void solve()
	{
		int n = ni();
		int m = ni();
		if(n < m){
			int d = n;n = m;m = d;
		}
		// n>=m
		if(m == 1){
			out.println(m-(m+2)/3);
			return;
		}

		int[][] dp = new int[n+1][1<<2*m];
		int[] fill = new int[1<<m];
		int mask = (1<<m)-1;
		for(int i = 0;i < 1<<m;i++){
			fill[i] = (i<<1|i|i>>1)&mask;
		}
		for(int i = 0;i < 1<<2*m;i++){
			int lower = i&mask;
			int upper = i>>m;
			dp[0][i] = (fill[lower]|upper) == mask ? Integer.bitCount(i) : 99999;
		}

		for(int i = 1;i <= n-2;i++){
			for(int j = 0;j < 1<<2*m;j++){
				int lower = j&mask;
				int upper = j>>m;

				int min = 99999;
				for(int k = 0;k < 1<<m;k++){
					if((upper|fill[lower]|k) == mask){
						min = Math.min(min, dp[i-1][fill[lower]<<m|k]);
					}
				}
				dp[i][j] = min + Integer.bitCount(upper);
			}
		}

		int gmin = 99999;
		for(int i = 0;i < 1<<2*m;i++){
			int lower = i&mask;
			int upper = i>>m;
			if((fill[upper]|lower) == mask){
				gmin = Math.min(gmin, dp[n-2][i]);
			}
		}
		out.println(n*m-gmin);
	}

	void run() throws Exception
	{
		in = oj ? new Scanner(System.in) : new Scanner(INPUT);
		out = new PrintWriter(System.out);

		long s = System.currentTimeMillis();
		solve();
		out.flush();
		tr(System.currentTimeMillis()-s+"ms");
	}

	public static void main(String[] args) throws Exception
	{
		new C3().run();
	}

	int ni() { return Integer.parseInt(in.next()); }
	long nl() { return Long.parseLong(in.next()); }
	double nd() { return Double.parseDouble(in.next()); }
	boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}

import java.io.InputStreamReader;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import java.math.BigInteger;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author AlexFetisov
 */
public class Main {
	public static void main(String[] args) {
		InputStream inputStream = System.in;
		OutputStream outputStream = System.out;
		InputReader in = new InputReader(inputStream);
		PrintWriter out = new PrintWriter(outputStream);
		TaskC solver = new TaskC();
		solver.solve(1, in, out);
		out.close();
	}
}

class TaskC {
    public void solve(int testNumber, InputReader in, PrintWriter out) {
        n = in.nextInt();
        m = in.nextInt();
        if (n > m) {
            int t = m;
            m = n;
            n = t;
        }
        if (n == 1) {
            out.println(m - ((m + 2) / 3));
        } else if (n == 2) {
            int cur = 2;
            for (int i = 2; i <= m; ++i) {
                if (i % 2 == 0 && i > 2) {
                    ++cur;
                }
            }
            out.println(n * m - cur);
        } else {
            f = new int[n][m];
            res = Integer.MAX_VALUE;
            cur = 0;
            step = 1;
            numFree = n * m;
            rec(0, 0);
            out.println(res);
        }
    }

    private void rec(int x, int y) {
        if (numFree == 0) {
            res = Math.min(res, cur);
            return;
        }
        if (x >= n) return;
        if (y >= m) {
            rec(x + 1, 0);
            return;
        }
        if (f[x][y] != 0) {
            rec(x, y + 1);
            return;
        }

        put(x, y);
        rec(x, y + 1);
        remove(x, y);

        if (isValid(x + 1, y)) {
            put(x + 1, y);
            rec(x, y + 1);
            remove(x + 1, y);
        }

        if (isValid(x, y + 1)) {
            put(x, y + 1);
            rec(x, y + 1);
            remove(x, y + 1);
        }
    }

    private void put(int x, int y) {
        for (int i = 0; i < 5; ++i) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (isValid(nx, ny)) {
                if (f[nx][ny] == 0) {
                    --numFree;
                    f[nx][ny] = step;
                }
            }
        }
        ++step;
        ++cur;
    }

    private void remove(int x, int y) {
        --step;
        for (int i = 0; i < 5; ++i) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (isValid(nx, ny)) {
                if (f[nx][ny] == step) {
                    ++numFree;
                    f[nx][ny] = 0;
                }
            }
        }
        --cur;
    }

    private boolean isValid(int x, int y) {
        return x >= 0 && y >= 0 && x < n && y < m;
    }

    int n, m;
    int[] dx = new int[] {-1, 0, 1, 0, 0};
    int[] dy = new int[] {0, 1, 0, -1, 0};

    int step;
    int numFree;
    int cur;
    int res;
    int[][] f;
}

class InputReader {
    private BufferedReader reader;
    private StringTokenizer stt;

    public InputReader(InputStream stream) {
        reader = new BufferedReader(new InputStreamReader(stream));
    }

    public String nextLine() {
        try {
            return reader.readLine().trim();
        } catch (IOException e) {
            return null;
        }
    }

    public String nextString() {
        while (stt == null || !stt.hasMoreTokens()) {
            stt = new StringTokenizer(nextLine());
        }
        return stt.nextToken();
    }

    public int nextInt() {
        return Integer.parseInt(nextString());
    }

}

/**
 * @author derrick20
 */
import java.io.*;
import java.util.*;

public class PetyaSpiders implements Runnable {
    public static void main(String[] args) throws Exception {
        new Thread(null, new PetyaSpiders(), ": )", 1 << 28).start();
    }

    public void run() {
        FastScanner sc = new FastScanner();
        PrintWriter out = new PrintWriter(System.out);

        int N = sc.nextInt();
        int M = sc.nextInt();
        if (N > M) {
            int temp = N;
            N = M;
            M = temp;
        }
        if (N == 1) {
            if (M == 1) {
                out.println(0);
            } else {
                out.println((M + 1) / 2);
            }
        } else {
            out.println(Math.max((N + 1) / 2 * M, (M + 1) / 2 * N));
        }
        out.close();
    }

    static class FastScanner {
        private int BS = 1 << 16;
        private char NC = (char) 0;
        private byte[] buf = new byte[BS];
        private int bId = 0, size = 0;
        private char c = NC;
        private double cnt = 1;
        private BufferedInputStream in;

        public FastScanner() {
            in = new BufferedInputStream(System.in, BS);
        }

        public FastScanner(String s) {
            try {
                in = new BufferedInputStream(new FileInputStream(new File(s)), BS);
            } catch (Exception e) {
                in = new BufferedInputStream(System.in, BS);
            }
        }

        private char getChar() {
            while (bId == size) {
                try {
                    size = in.read(buf);
                } catch (Exception e) {
                    return NC;
                }
                if (size == -1) return NC;
                bId = 0;
            }
            return (char) buf[bId++];
        }

        public int nextInt() {
            return (int) nextLong();
        }

        public int[] nextInts(int N) {
            int[] res = new int[N];
            for (int i = 0; i < N; i++) {
                res[i] = (int) nextLong();
            }
            return res;
        }

        public long[] nextLongs(int N) {
            long[] res = new long[N];
            for (int i = 0; i < N; i++) {
                res[i] = nextLong();
            }
            return res;
        }

        public long nextLong() {
            cnt = 1;
            boolean neg = false;
            if (c == NC) c = getChar();
            for (; (c < '0' || c > '9'); c = getChar()) {
                if (c == '-') neg = true;
            }
            long res = 0;
            for (; c >= '0' && c <= '9'; c = getChar()) {
                res = (res << 3) + (res << 1) + c - '0';
                cnt *= 10;
            }
            return neg ? -res : res;
        }

        public double nextDouble() {
            double cur = nextLong();
            return c != '.' ? cur : cur + nextLong() / cnt;
        }

        public double[] nextDoubles(int N) {
            double[] res = new double[N];
            for (int i = 0; i < N; i++) {
                res[i] = nextDouble();
            }
            return res;
        }

        public String next() {
            StringBuilder res = new StringBuilder();
            while (c <= 32) c = getChar();
            while (c > 32) {
                res.append(c);
                c = getChar();
            }
            return res.toString();
        }

        public String nextLine() {
            StringBuilder res = new StringBuilder();
            while (c <= 32) c = getChar();
            while (c != '\n') {
                res.append(c);
                c = getChar();
            }
            return res.toString();
        }

        public boolean hasNext() {
            if (c > 32) return true;
            while (true) {
                c = getChar();
                if (c == NC) return false;
                else if (c > 32) return true;
            }
        }
    }

    static void ASSERT(boolean assertion, String message) {
        if (!assertion) throw new AssertionError(message);
    }

    static void ASSERT(boolean assertion) {
        if (!assertion) throw new AssertionError();
    }
}

/**
 * @author derrick20
 */
import java.io.*;
import java.util.*;

public class PetyaSpiders implements Runnable {
    public static void main(String[] args) throws Exception {
        new Thread(null, new PetyaSpiders(), ": )", 1 << 28).start();
    }

    public void run() {
        FastScanner sc = new FastScanner();
        PrintWriter out = new PrintWriter(System.out);

        N = sc.nextInt();
        M = sc.nextInt();
        if (N > M) {
            int temp = N;
            N = M;
            M = temp;
        }
        if (N == 1) {
            if (M == 1) {
                out.println(0);
            } else {
                out.println((M + 1) / 2);
            }
        } else {
            int[][][] dp = new int[M][1 << N][1 << N];
            // N, M are at least 2. N <= 6
            // base case: try all prev and curr masks, let next = 0
            for (int prev = 0; prev < 1 << N; prev++) {
                for (int curr = 0; curr < 1 << N; curr++) {
                    if (isValid(0, prev, curr)) {
                        dp[1][prev][curr] = Integer.bitCount(prev) + Integer.bitCount(curr);
                    } else {
                        dp[1][prev][curr] = oo;
                    }
                }
            }
            for (int col = 2; col <= M - 2; col++) {
                for (int next = 0; next < 1 << N; next++) {
                    for (int curr = 0; curr < 1 << N; curr++) {
                        dp[col][curr][next] = oo;
                        for (int prev = 0; prev < 1 << N; prev++) {
                            if (dp[col - 1][prev][curr] != oo && isValid(prev, curr, next)) {
                                dp[col][curr][next] = Math.min(dp[col][curr][next], dp[col - 1][prev][curr] + Integer.bitCount(next));
                            }
                        }
                    }
                }
            }
            // to get the answer, we need to act on the last column by trying to
            // ensure that both the prev row AND the current row are valid
            // basically, isValid(prev, curr, next) && isValid(curr, next, 0)
            int ans = oo;
            for (int next = 0; next < 1 << N; next++) {
                for (int curr = 0; curr < 1 << N; curr++) {
                    dp[M - 1][curr][next] = oo;
                    for (int prev = 0; prev < 1 << N; prev++) {
                        if (dp[M - 2][prev][curr] != oo && isValid(prev, curr, next) && isValid(curr, next, 0)) {
                            dp[M - 1][curr][next] = Math.min(dp[M - 1][curr][next], dp[M - 2][prev][curr] + Integer.bitCount(next));
                            ans = Math.min(ans, dp[M - 1][curr][next]);
                        }
                    }
                }
            }
//            for (int c = 1; c < M; c++) {
//                System.out.println("For col = " + c);
//                for (int prev = 0; prev < 1 << N; prev++) {
//                    System.out.println("Prev = " + Integer.toBinaryString(prev));
//                    for (int curr = 0; curr < 1 << N; curr++) {
//                        System.out.println("Curr = " + Integer.toBinaryString(curr) + " gives " + dp[c][prev][curr] + " ");
//                    }
//                }
//            }
            out.println(N * M - ans);
        }
        out.close();
    }

    static int N, M;
    static int oo = 999;
    static int[] dr = {1, 0, -1, 0}, dc = {0, 1, 0, -1};

    static boolean isValid(int prev, int curr, int next) {
        boolean[][] grid = new boolean[N][3];
        int[] subsets = {prev, curr, next};
        for (int r = 0; r < N; r++) {
            for (int c = 0; c < 3; c++) {
                if ((subsets[c] & 1) > 0) {
                    grid[r][c] = true;
                    for (int k = 0; k < 4; k++) {
                        int r2 = r + dr[k];
                        int c2 = c + dc[k];
                        if (0 <= r2 && r2 <= N - 1 && 0 <= c2 && c2 <= 2) {
                            grid[r2][c2] = true;
                        }
                    }
                }
                subsets[c] >>= 1;
            }
        }
        for (int r = 0; r < N; r++) {
            if (!grid[r][1]) {
                // we only need to ensure the middle is safe
                return false;
            }
        }
        return true;
    }

    static class FastScanner {
        private int BS = 1 << 16;
        private char NC = (char) 0;
        private byte[] buf = new byte[BS];
        private int bId = 0, size = 0;
        private char c = NC;
        private double cnt = 1;
        private BufferedInputStream in;

        public FastScanner() {
            in = new BufferedInputStream(System.in, BS);
        }

        public FastScanner(String s) {
            try {
                in = new BufferedInputStream(new FileInputStream(new File(s)), BS);
            } catch (Exception e) {
                in = new BufferedInputStream(System.in, BS);
            }
        }

        private char getChar() {
            while (bId == size) {
                try {
                    size = in.read(buf);
                } catch (Exception e) {
                    return NC;
                }
                if (size == -1) return NC;
                bId = 0;
            }
            return (char) buf[bId++];
        }

        public int nextInt() {
            return (int) nextLong();
        }

        public int[] nextInts(int N) {
            int[] res = new int[N];
            for (int i = 0; i < N; i++) {
                res[i] = (int) nextLong();
            }
            return res;
        }

        public long[] nextLongs(int N) {
            long[] res = new long[N];
            for (int i = 0; i < N; i++) {
                res[i] = nextLong();
            }
            return res;
        }

        public long nextLong() {
            cnt = 1;
            boolean neg = false;
            if (c == NC) c = getChar();
            for (; (c < '0' || c > '9'); c = getChar()) {
                if (c == '-') neg = true;
            }
            long res = 0;
            for (; c >= '0' && c <= '9'; c = getChar()) {
                res = (res << 3) + (res << 1) + c - '0';
                cnt *= 10;
            }
            return neg ? -res : res;
        }

        public double nextDouble() {
            double cur = nextLong();
            return c != '.' ? cur : cur + nextLong() / cnt;
        }

        public double[] nextDoubles(int N) {
            double[] res = new double[N];
            for (int i = 0; i < N; i++) {
                res[i] = nextDouble();
            }
            return res;
        }

        public String next() {
            StringBuilder res = new StringBuilder();
            while (c <= 32) c = getChar();
            while (c > 32) {
                res.append(c);
                c = getChar();
            }
            return res.toString();
        }

        public String nextLine() {
            StringBuilder res = new StringBuilder();
            while (c <= 32) c = getChar();
            while (c != '\n') {
                res.append(c);
                c = getChar();
            }
            return res.toString();
        }

        public boolean hasNext() {
            if (c > 32) return true;
            while (true) {
                c = getChar();
                if (c == NC) return false;
                else if (c > 32) return true;
            }
        }
    }

    static void ASSERT(boolean assertion, String message) {
        if (!assertion) throw new AssertionError(message);
    }

    static void ASSERT(boolean assertion) {
        if (!assertion) throw new AssertionError();
    }
}

import java.io.*;
import java.util.*;

public class Main
{
	static final boolean _DEBUG = false;

	private static class MyScanner {
		BufferedReader br;
		StringTokenizer st;

		public MyScanner(BufferedReader _br) {
			br = _br;
		}

		String next() {
			while (st == null || !st.hasMoreElements()) {
				try {
					st = new StringTokenizer(br.readLine());
				} catch (Exception e) {
					e.printStackTrace();
					return "";
				}
			}
			return st.nextToken();
		}

		int nextInt() {
			return Integer.parseInt(next());
		}

		long nextLong() {
			return Long.parseLong(next());
		}

		double nextDouble() {
			return Double.parseDouble(next());
		}

		String nextLine() {
			String str = "";
			try {
				str = br.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return str;
		}

	}

	static MyScanner   scan;
	static PrintWriter out;

	static int debugCount = 0;
	static void debug(String msg) {
		if (_DEBUG && debugCount < 200) {
			out.println(msg);
			out.flush();
			debugCount++;
		}
	}

    public static void main (String args[]) throws IOException {
//    	scan = new MyScanner(new BufferedReader(new FileReader("test.in")));
    	scan = new MyScanner(new BufferedReader(new InputStreamReader(System.in)));
    	out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
        Main inst = new Main();
        inst.execute();
        out.close();
    }

    int N, M, pow;
    int[] counts;
    int[][][] dp;

    void execute() {
    	N = scan.nextInt();
    	M = scan.nextInt();
    	if (N < M) {
    		int temp = N;
    		N = M;
    		M = temp;
    	}
    	pow = 1<<M;
    	counts = new int[pow];
    	dp = new int[N][pow][pow];
    	for (int[][] i : dp) {
    		for (int[] j : i) {
    			Arrays.fill(j, -1);
    		}
    	}
    	for (int i = 0; i < pow; i++) {
    		counts[i] = Integer.bitCount(i);
    		if (((i|(i<<1)|(i>>1))&(pow-1))==(pow-1)) {
        		dp[0][i][0] = counts[i];
    		}
    	}
    	int spiders = Integer.MAX_VALUE;
    	for (int y = 0; y < N-1; y++) {
    		for (int i = 0; i < pow; i++) {
    			for (int j = 0; j < pow; j++) {
    				if (dp[y][i][j] == -1) {
    					continue;
    				}
    				for (int k = 0; k < pow; k++) {
    					if (((i|(i<<1)|(i>>1)|j|k)&(pow-1))==(pow-1)) {
    						int value = dp[y][i][j] + counts[k];
    						if (dp[y+1][k][i] == -1 || dp[y+1][k][i] > value) {
    							dp[y+1][k][i] = value;
    						}
    					}
    				}
    			}
    		}
    	}
//    	System.out.println(Arrays.deepToString(dp));
    	for (int i = 0; i < pow; i++) {
    		for (int j = 0; j < pow; j++) {
    			if (dp[N-1][i][j] != -1 && ((i|(i<<1)|(i>>1)|j)&(pow-1))==(pow-1)) {
    				spiders = Math.min(spiders, dp[N-1][i][j]);
    			}
    		}
    	}
    	out.println((N*M)-spiders);
    }
}

/*************************************************************************
    > File Name: cf111c.java
    > Author: Yuchen Wang
    > Mail: wyc8094@gmail.com
    > Created Time: Sat Feb 11 16:51:01 2017
 ************************************************************************/

import java.util.*;
import java.io.*;

public class cf111c
{
	public static int n,m,maxm;
	public static int[][][] dp;
	public static int[] s;

	public static int cal(int cur)
	{
		int res = 0;
		while(cur>0){
			res ++;
			cur = cur&(cur-1);
		}
		return res;
	}

	public static boolean check(int a,int b,int c)
	{
		int res = (maxm-1) & (a | (a<<1) | (a>>1) | b | c);
		if(res == maxm-1)return true;
		else return false;
	}

	public static void main(String[] argv)
	{
		Scanner in = new Scanner(System.in);

		n = in.nextInt();
		m = in.nextInt();
		if(n<m){
			int t = m;
			m = n;
			n = t;
		}

		maxm = 1<<m;
		int i,j,k,l;
		dp = new int[n+1][1<<m][1<<m];
		s = new int[1<<m];

		for(i=0;i<n+1;i++){
			for(j=0;j<maxm;j++){
				Arrays.fill(dp[i][j],100);
			}
		}

		for(i=0;i<maxm;i++){
			s[i] = cal(i);
			dp[0][0][i] = 0;
		}

		for(i=1;i<=n;i++){
			for(j=0;j<maxm;j++){
				for(k=0;k<maxm;k++){
					for(l=0;l<maxm;l++){
						if(dp[i-1][k][l]!=100 && check(k,l,j)){
							dp[i][l][j] = Math.min(dp[i-1][k][l]+s[l],dp[i][l][j]);
						}
					}
				}
			}
		}

		int ans = 100;
		for(i=0;i<maxm;i++)
			ans = Math.min(dp[n][i][0],ans);

		System.out.println(n*m-ans);
		return;
	}
}

/*************************************************************************
    > File Name: cf111c.java
    > Author: Yuchen Wang
    > Mail: wyc8094@gmail.com
    > Created Time: Sat Feb 11 16:51:01 2017
 ************************************************************************/

import java.util.*;
import java.io.*;

public class cf111c
{
	public static int n,m,maxm;
	public static int[][][] dp;
	public static int[] s;

	public static int cal(int cur)
	{
		int res = 0;
		while(cur>0){
			res ++;
			cur = cur&(cur-1);
		}
		return res;
	}

	public static boolean check(int a,int b,int c)
	{
		int res = (maxm-1) & (a | (a<<1) | (a>>1) | b | c);
		if(res == maxm-1)return true;
		else return false;
	}

	public static void main(String[] argv)
	{
		Scanner in = new Scanner(System.in);

		n = in.nextInt();
		m = in.nextInt();
		if(n<m){
			int t = m;
			m = n;
			n = t;
		}

		maxm = 1<<m;
		int i,j,k,l;
		dp = new int[n+1][1<<m][1<<m];
		s = new int[1<<m];

		for(i=0;i<n+1;i++){
			for(j=0;j<maxm;j++){
				Arrays.fill(dp[i][j],100);
			}
		}
		for(i=0;i<maxm;i++){
			s[i] = cal(i);
			dp[0][0][i] = 0;
		}

		for(i=1;i<=n;i++){
			for(j=0;j<maxm;j++){
				for(k=0;k<maxm;k++){
					for(l=0;l<maxm;l++){
						if(dp[i-1][k][l]>=0 && check(k,l,j)){
							dp[i][l][j] = Math.min(dp[i-1][k][l]+s[l],dp[i][l][j]);
						}
					}
				}
			}
		}

		int ans = 0;
		for(i=0;i<maxm;i++)
			ans = Math.max(dp[n][i][0],ans);

		System.out.println(ans);
		return;
	}
}

/*
If you want to aim high, aim high
Don't let that studying and grades consume you
Just live life young
******************************
If I'm the sun, you're the moon
Because when I go up, you go down
*******************************
I'm working for the day I will surpass you
https://www.a2oj.com/Ladder16.html
*/
import java.util.*;
import java.io.*;
import java.math.*;

   public class x111C
   {
      public static void main(String omkar[]) throws Exception
      {
         BufferedReader infile = new BufferedReader(new InputStreamReader(System.in));
         StringTokenizer st = new StringTokenizer(infile.readLine());
         int N = Integer.parseInt(st.nextToken());
         int M = Integer.parseInt(st.nextToken());
         if(N*M == 1)
         {
            System.out.println(0);
            return;
         }
         int T = 5000;
         int res = 0;
         while(T-->0)
         {
            int[][] grid = gen(N, M);
            int temp = 0;
            for(int i=0; i < N; i++)
               for(int j=0; j < M; j++)
                  if(grid[i][j] == 0)
                     temp++;
            res = Math.max(res, temp);
         }
         System.out.println(res);
      }
      public static int[][] gen(int N, int M)
      {
         int[][] grid = new int[N][M];
         for(int i=0; i < N; i++)
            Arrays.fill(grid[i], 1);
         for(int i=0; i < N; i++)
            for(int j=0; j < M; j++)
            {
               int boof = (int)(Math.random()*4);
               while(!bad(i, j, N, M, boof))
                  boof = (int)(Math.random()*4);
               int x = i;
               int y = j;
               if(boof == 0)
                  grid[x-1][y]++;
               else if(boof == 1)
                  grid[x][y-1]++;
               else if(boof == 2)
                  grid[x+1][y]++;
               else
                  grid[x][y+1]++;
               grid[x][y]--;
            }
         return grid;
      }
      public static boolean bad(int x, int y, int N, int M, int boof)
      {
         if(boof == 0)
            return x > 0;
         if(boof == 1)
            return y > 0;
         if(boof == 2)
            return x+1 < N;
         return y+1 < M;
      }
   }

/*
If you want to aim high, aim high
Don't let that studying and grades consume you
Just live life young
******************************
If I'm the sun, you're the moon
Because when I go up, you go down
*******************************
I'm working for the day I will surpass you
https://www.a2oj.com/Ladder16.html
*/
import java.util.*;
import java.io.*;
import java.math.*;

   public class x111C
   {
      public static void main(String omkar[]) throws Exception
      {
         BufferedReader infile = new BufferedReader(new InputStreamReader(System.in));
         StringTokenizer st = new StringTokenizer(infile.readLine());
         int N = Integer.parseInt(st.nextToken());
         int M = Integer.parseInt(st.nextToken());
         if(N*M == 1)
         {
            System.out.println(0);
            return;
         }
         int T = 500000;
         int res = 0;
         while(T-->0)
         {
            int[][] grid = gen(N, M);
            int temp = 0;
            for(int i=0; i < N; i++)
               for(int j=0; j < M; j++)
                  if(grid[i][j] == 0)
                     temp++;
            res = Math.max(res, temp);
         }
         System.out.println(res);
      }
      public static int[][] gen(int N, int M)
      {
         int[][] grid = new int[N][M];
         for(int i=0; i < N; i++)
            Arrays.fill(grid[i], 1);
         for(int i=0; i < N; i++)
            for(int j=0; j < M; j++)
            {
               int boof = (int)(Math.random()*5);
               while(!bad(i, j, N, M, boof))
                  boof = (int)(Math.random()*5);
               int x = i;
               int y = j;
               if(boof == 0)
                  grid[x-1][y]++;
               else if(boof == 1)
                  grid[x][y-1]++;
               else if(boof == 2)
                  grid[x+1][y]++;
               else if(boof == 3)
                  grid[x][y+1]++;
               else
                  grid[x][y]++;
               grid[x][y]--;
            }
         return grid;
      }
      public static boolean bad(int x, int y, int N, int M, int boof)
      {
         if(boof == 0)
            return x > 0;
         if(boof == 1)
            return y > 0;
         if(boof == 2)
            return x+1 < N;
         if(boof == 4) //stay where you are
            return true;
         return y+1 < M;
      }
   }

/*
If you want to aim high, aim high
Don't let that studying and grades consume you
Just live life young
******************************
If I'm the sun, you're the moon
Because when I go up, you go down
*******************************
I'm working for the day I will surpass you
https://www.a2oj.com/Ladder16.html
*/
import java.util.*;
import java.io.*;
import java.math.*;

   public class x111C
   {
      public static void main(String omkar[]) throws Exception
      {
         BufferedReader infile = new BufferedReader(new InputStreamReader(System.in));
         StringTokenizer st = new StringTokenizer(infile.readLine());
         int N = Integer.parseInt(st.nextToken());
         int M = Integer.parseInt(st.nextToken());
         if(N*M == 1)
         {
            System.out.println(0);
            return;
         }
         int T = 50000;
         int res = 0;
         while(T-->0)
         {
            int[][] grid = gen(N, M);
            int temp = 0;
            for(int i=0; i < N; i++)
               for(int j=0; j < M; j++)
                  if(grid[i][j] == 0)
                     temp++;
            res = Math.max(res, temp);
         }
         System.out.println(res);
      }
      public static int[][] gen(int N, int M)
      {
         int[][] grid = new int[N][M];
         for(int i=0; i < N; i++)
            Arrays.fill(grid[i], 1);
         for(int i=0; i < N; i++)
            for(int j=0; j < M; j++)
            {
               int boof = (int)(Math.random()*4);
               while(!bad(i, j, N, M, boof))
                  boof = (int)(Math.random()*4);
               int x = i;
               int y = j;
               if(boof == 0)
                  grid[x-1][y]++;
               else if(boof == 1)
                  grid[x][y-1]++;
               else if(boof == 2)
                  grid[x+1][y]++;
               else
                  grid[x][y+1]++;
               grid[x][y]--;
            }
         return grid;
      }
      public static boolean bad(int x, int y, int N, int M, int boof)
      {
         if(boof == 0)
            return x > 0;
         if(boof == 1)
            return y > 0;
         if(boof == 2)
            return x+1 < N;
         return y+1 < M;
      }
   }

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner r = new Scanner(System.in);

		int n = r.nextInt();
		int m = r.nextInt();

		if(n > m){int t = n; n = m; m = t;}

		dp = new int[m+1][1 << 7][1 << 7];
		int min = go(m, 0, (1<<n) -1, n, m);

		System.out.println(n * m - min);
	}
	static int[][][] dp;
	private static int go(int rem, int prev, int need, int n, int m) {
//		for(int i = rem; i < m; i++)
//			System.out.print(" ");
//		System.out.println(rem + ": " + prev +","+need);
		if(rem == 0)return prev == 0?0:1 << 20;
		if(dp[rem][prev][need] != -1)return dp[rem][prev][need];

		int min = 1 << 20;
		for(int now = 0; now < 1 << n; now++){
			if((~now & prev) != 0)continue;

			int after = need & ~(now) & ~(now << 1) & ~(now >> 1);
			int next = ~(now) & ((1 << n)-1);

			int current = Integer.bitCount(now) + go(rem-1, after ,next, n, m);
			min = Math.min(min, current);
		}
		return dp[rem][prev][need] = min;
	}
}

import static java.util.Arrays.*;
import static java.lang.Math.*;
import static java.math.BigInteger.*;
import java.util.*;
import java.math.*;
import java.io.*;

public class C implements Runnable
{
    String file = "input";

    boolean TEST = false;

    void solve() throws IOException
    {
        rows = nextInt();
        cols = nextInt();
        if(cols > rows)
        {
            int t = rows; rows = cols; cols = t;
        }
        dp = new int[rows][cols][1 << cols][1 << cols][1 << cols];
        for(int i = 0; i < rows; i++)
            for(int j = 0; j < cols; j++)
                for(int k = 0; k < 1 << cols; k++)
                    for(int u = 0; u < 1 << cols; u++)
                        for(int v = 0; v < 1 << cols; v++) dp[i][j][k][u][v] = -1;
        out.println(go(0, 0, 0, 0, 0));
    }
    int rows, cols;
    int[][][][][] dp;
    int INF = 1 << 20;
    int go(int i, int j, int a, int b, int c)
    {
        if(i == rows)
        {
            return a == 0 && b == 0 && c == 0 ? 0 : -INF;
        }
        if(i + 1 == rows && b != 0 && c != 0) return -INF;
        if(i + 2 == rows && c != 0) return -INF;
        if(j == cols)
        {
            return go(i + 1, 0, b, c, 0);
        }

        if(dp[i][j][a][b][c] != -1) return dp[i][j][a][b][c];

        if(!test(a, j, -1, -1)) return go(i, j + 1, a, b, c);

        int res = -INF;

        //1
        if(test(a, j, -1, -1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), b, c));
        //2
        if(test(a, j, -1, -1) && test(b, j, -1, -1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), set(b, j, -1, -1), c) + 1);
        //3
        if(j + 2 <= cols && test(a, j, j + 1, -1))
            res = max(res, go(i, j + 2, set(a, j, j + 1, -1), b, c) + 1);
        //4
        if(j + 3 <= cols && test(a, j, j + 1, j + 2))
            res = max(res, go(i, j + 2, set(a, j, j + 1, j + 2), b, c) + 2);
        //5
        if(test(a, j, -1, -1) && test(b, j, -1, -1) && test(c, j, -1, -1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), set(b, j, -1, -1), set(c, j, -1, -1)) + 2);
        //6
        if(j - 1 >= 0 && test(a, j, -1, -1) && test(b, j - 1, j, -1) && test(c, j, -1, -1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), set(b, j - 1, j, -1), set(c, j, -1, -1)) + 3);
        //7
        if(j + 2 <= cols && test(a, j, -1, -1) && test(b, j, j + 1, -1) && test(c, j, -1, -1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), set(b, j, j + 1, -1), set(c, j, -1, -1)) + 3);
        //8
        if(j + 3 <= cols && test(a, j, j + 1, j + 2) && test(b, j + 1, -1, -1))
            res = max(res, go(i, j + 3, set(a, j, j + 1, j + 2), set(b, j + 1, -1, -1), c) + 3);
        //9
        if(j + 2 <= cols && j - 1 >= 0 && test(b, j - 1, j, j + 1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), set(b, j - 1, j, j + 1), c) + 3);
        //10
        if(j + 2 <= cols && j - 1 >= 0 && test(b, j - 1, j, j + 1) && test(c, j, -1, -1))
            res = max(res, go(i, j + 1, set(a, j, -1, -1), set(b, j - 1, j, j + 1), set(c, j, -1, -1)) + 4);

        return dp[i][j][a][b][c] = res;
    }
    int set(int a, int i, int j, int k)
    {
        if(i != -1) a |= 1 << i;
        if(j != -1) a |= 1 << j;
        if(k != -1) a |= 1 << k;
        return a;
    }
    boolean test(int a, int i, int j, int k)
    {
        if(i != -1 && (a >> i & 1) != 0) return false;
        if(j != -1 && (a >> j & 1) != 0) return false;
        if(k != -1 && (a >> k & 1) != 0) return false;
        return true;
    }

    String next() throws IOException
    {
        while(st == null || !st.hasMoreTokens()) st = new StringTokenizer(input.readLine());
        return st.nextToken();
    }

    int nextInt() throws IOException
    {
        return Integer.parseInt(next());
    }

    long nextLong() throws IOException
    {
        return Long.parseLong(next());
    }

    double nextDouble() throws IOException
    {
        return Double.parseDouble(next());
    }

    void print(Object... o)
    {
        System.out.println(deepToString(o));
    }

    void gcj(Object o)
    {
        String s = String.valueOf(o);
        out.println("Case #" + test + ": " + s);
        System.out.println("Case #" + test + ": " + s);
    }

    BufferedReader input;
    PrintWriter out;
    StringTokenizer st;
    int test;

    void init() throws IOException
    {
        if(TEST) input = new BufferedReader(new FileReader(file + ".in"));
        else input = new BufferedReader(new InputStreamReader(System.in));
        out = new PrintWriter(new BufferedOutputStream(System.out));
    }

    public static void main(String[] args) throws IOException
    {
        new Thread(null, new C(), "", 1 << 20).start();
    }

    public void run()
    {
        try
        {
            init();
            if(TEST)
            {
                int runs = nextInt();
                for(int i = 0; i < runs; i++) solve();
            }
            else solve();
            out.close();
        }
        catch(Exception e)
        {
            e.printStackTrace();
            System.exit(1);
        }
    }
}

import static java.util.Arrays.*;
import static java.lang.Math.*;
import static java.math.BigInteger.*;
import java.util.*;
import java.math.*;
import java.io.*;

public class C implements Runnable
{
    String file = "input";

    boolean TEST = false;

    void solve() throws IOException
    {
        int n = nextInt(), m = nextInt();
        int k = m / 3;
        int res = 0;
        res += k * n * 2;

        int q = m % 3;
        if(q == 1) res += f(n);
        if(q == 2)
        {
            res += 2 * f(n / 3 * 3);
            int K = n / 3;
            if(K == 1) res++;
            if(K == 2) res += 2;
        }
        out.println(res);
    }
    int f(int n)
    {
        int k = n / 3;
        int q = n % 3;
        return k * 2 + (q == 0 ? 0 : q == 1 ? 0 : 1);
    }

    String next() throws IOException
    {
        while(st == null || !st.hasMoreTokens()) st = new StringTokenizer(input.readLine());
        return st.nextToken();
    }

    int nextInt() throws IOException
    {
        return Integer.parseInt(next());
    }

    long nextLong() throws IOException
    {
        return Long.parseLong(next());
    }

    double nextDouble() throws IOException
    {
        return Double.parseDouble(next());
    }

    void print(Object... o)
    {
        System.out.println(deepToString(o));
    }

    void gcj(Object o)
    {
        String s = String.valueOf(o);
        out.println("Case #" + test + ": " + s);
        System.out.println("Case #" + test + ": " + s);
    }

    BufferedReader input;
    PrintWriter out;
    StringTokenizer st;
    int test;

    void init() throws IOException
    {
        if(TEST) input = new BufferedReader(new FileReader(file + ".in"));
        else input = new BufferedReader(new InputStreamReader(System.in));
        out = new PrintWriter(new BufferedOutputStream(System.out));
    }

    public static void main(String[] args) throws IOException
    {
        new Thread(null, new C(), "", 1 << 20).start();
    }

    public void run()
    {
        try
        {
            init();
            if(TEST)
            {
                int runs = nextInt();
                for(int i = 0; i < runs; i++) solve();
            }
            else solve();
            out.close();
        }
        catch(Exception e)
        {
            e.printStackTrace();
            System.exit(1);
        }
    }
}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;

import static java.lang.Math.*;
import static java.util.Arrays.*;

public class C{
    Scanner sc=new Scanner(System.in);

    int INF=1<<28;
    double EPS=1e-9;

    int n, m;

    void run(){
        n=sc.nextInt();
        m=sc.nextInt();
        solve();
    }

    void solve(){
        int n1=(n+2)/3;
        int m1=(m+2)/3;
        int ans=INF;
        if(m%2==0){
            ans=min(ans, n1*m);
        }else{
            ans=min(ans, n1*(m-1)+n);
        }
        if(n%2==0){
            ans=min(ans, n*m1);
        }else{
            ans=min(ans, (n-1)*m1+m);
        }

        int min=min(n1*m, n*m1);
        min=n*m-min;
        println(min+"");
    }

    void println(String s){
        System.out.println(s);
    }

    void print(String s){
        System.out.print(s);
    }

    void debug(Object... os){
        System.err.println(Arrays.deepToString(os));
    }

    public static void main(String[] args){
        new C().run();
    }
}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;

import static java.lang.Math.*;
import static java.util.Arrays.*;

public class C{
    Scanner sc=new Scanner(System.in);

    int INF=1<<28;
    double EPS=1e-9;

    int n, m;

    void run(){
        n=sc.nextInt();
        m=sc.nextInt();
        solve();
    }

    void solve(){
        int n1=(n+2)/3;
        int m1=(m+2)/3;
        int min=min(n1*m, n*m1);
        min=n*m-min;
        println(min+"");
    }

    void println(String s){
        System.out.println(s);
    }

    void print(String s){
        System.out.print(s);
    }

    void debug(Object... os){
        System.err.println(Arrays.deepToString(os));
    }

    public static void main(String[] args){
        new C().run();
    }
}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;

import static java.lang.Math.*;
import static java.util.Arrays.*;

public class C{
    Scanner sc=new Scanner(System.in);

    int INF=1<<28;
    double EPS=1e-9;

    int n, m;

    void run(){
        n=sc.nextInt();
        m=sc.nextInt();
        solve();
    }

    void solve(){
        if(n>m){
            int t=n;
            n=m;
            m=t;
        }
        int full=(1<<m)-1;
        int[][] dp=new int[1<<m][1<<m];
        int[][] tmp=new int[1<<m][1<<m];
        for(int i=0; i<1<<m; i++){
            fill(dp[i], INF);
        }
        for(int i=0; i<1<<m; i++){
            int b1=(i|(i>>1)|(i<<1))&full;
            int b2=i;
            dp[b1][b2]=Integer.bitCount(i);
            debug(Integer.toBinaryString(b1), dp[b1]);
        }
        debug();
        for(int j=0; j<n-1; j++){
            for(int i=0; i<1<<m; i++){
                System.arraycopy(dp[i], 0, tmp[i], 0, 1<<m);
                fill(dp[i], INF);
            }

            for(int b1=0; b1<1<<m; b1++){
                for(int b2=0; b2<1<<m; b2++){

                    for(int i=0; i<1<<m; i++){
                        if((b1|i)!=full){
                            continue;
                        }
                        if(false)
                            debug(Integer.toBinaryString(b1),
                                    Integer.toBinaryString(b2),
                                    Integer.toBinaryString(i));
                        int b=(i|(i>>1)|(i<<1))&full;
                        dp[b2|b][i]=min(dp[b2|b][i],
                                tmp[b1][b2]+Integer.bitCount(i));
                    }
                }
            }
            debug(j);
            for(int i=0; i<1<<m; i++){
                debug(Integer.toBinaryString(i), dp[i]);
            }
        }

        int min=INF;
        for(int i=0; i<1<<m; i++){
            min=min(min, dp[full][i]);
        }
        debug(min);
        int ans=m*n-min;
        println(ans+"");
    }

    void println(String s){
        System.out.println(s);
    }

    void print(String s){
        System.out.print(s);
    }

    void debug(Object... os){
        // System.err.println(Arrays.deepToString(os));
    }

    public static void main(String[] args){
        new C().run();
    }
}

import java.util.*;
import java.lang.*;
import java.math.*;
import java.io.*;

import static java.lang.Math.*;
import static java.util.Arrays.*;

public class C{
    Scanner sc=new Scanner(System.in);

    int INF=1<<28;
    double EPS=1e-9;

    int n, m;

    void run(){
        n=sc.nextInt();
        m=sc.nextInt();
        solve();
    }

    void solve(){
        int n1=(n+2)/3;
        int m1=(m+2)/3;
        int ans=INF;
        if(m%2==0){
            ans=min(ans, n1*m);
        }else{
            ans=min(ans, n1*(m-1)+n);
        }
        if(n%2==0){
            ans=min(ans, n*m1);
        }else{
            ans=min(ans, (n-1)*m1+m);
        }
        ans=n*m-ans;
        println(ans+"");
    }

    void println(String s){
        System.out.println(s);
    }

    void print(String s){
        System.out.print(s);
    }

    void debug(Object... os){
        System.err.println(Arrays.deepToString(os));
    }

    public static void main(String[] args){
        new C().run();
    }
}

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import static java.lang.Math.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import java.util.TreeMap;

/**
 *
 * @author pttrung
 */
public class C {

    public static double Epsilon = 1e-6;
    public static long x, y, d;
    public static long MOD = 1000000007;
    public static int[][][] dp;
    public static int min, max, need;

    public static void main(String[] args) {

        Scanner in = new Scanner();
        PrintWriter out = new PrintWriter(System.out);

        int n = in.nextInt();
        int m = in.nextInt();
        min = Math.min(n, m);
        max = (m + n) - min;
        dp = new int[max][1 << min][1 << min];

        for (int[][] temp : dp) {
            for (int[] val : temp) {
                Arrays.fill(val, -1);
            }
        }
        need = (1 << min) - 1;
        //System.out.println(add + total);
        //  System.out.println(min + " " + max);

        out.println(cal(0, 0, 0));

        out.close();

    }

    public static int cal(int index, int last, int lastHold) {

        if (index == max) {
            return 0;
        }
        if (dp[index][lastHold][last] != -1) {
            return dp[index][lastHold][last];
        }
        int result = 0;

        for (int i = 0; i < 1 << min; i++) {
            if ((i | last) == need || (index == 0)) {
              //  System.out.println("PREV " + index + " " + i + " " + last + " " + lastHold);
              //  System.out.println("NEXT " + index + " " + i + " " + (i | lastHold) + " " + i);
                //   System.out.println(Integer.bitCount(i) + " " + i);

//                if (index == 3) {
//                    System.out.println(last + " " + i + " " + match(i, last) + " " + next);
//                }
                if(index + 1 == max && !match(i,lastHold)){
                    continue;
                }
                int temp = cal(index + 1, i | lastHold, i) + (min - Integer.bitCount(i));
                result = result < temp ? temp : result;

                //  break;
            }
        }
        dp[index][lastHold][last] = result;
        return result;
    }

    public static boolean match(int mask, int last) {
        int result = last;
        for (int i = 0; i < min; i++) {
            if (((1 << i) & mask) != 0) {
                int a = i - 1;
                int b = i + 1;
                result |= (1 << i);
                if (a >= 0) {
                    result |= (1 << a);
                }
                if (b < min) {
                    result |= (1 << b);
                }
            }
        }
        // System.out.println(mask + " " + result + " " + need);
        return result == need;
    }

    public static long pow(long a, long b) {
        if (b == 0) {
            return 1;
        }
        long val = pow(a, b / 2);
        if (b % 2 == 0) {
            return val * val % MOD;
        } else {
            return a * (val * val % MOD) % MOD;
        }
    }

    public static void extendEuclid(long a, long b) {
        if (b == 0) {
            x = 1;
            y = 0;
            d = a;
            return;
        }
        extendEuclid(b, a % b);
        long x1 = y;
        long y1 = x - (a / b) * y;
        x = x1;
        y = y1;

    }

    public static long gcd(long a, long b) {
        if (b == 0) {
            return a;
        }
        return gcd(b, a % b);
    }

    static class Line {

        double a, b, c;

        Line(double x1, double y1, double x2, double y2) {
            if (Math.abs(x1 - x2) < Epsilon && Math.abs(y1 - y2) < Epsilon) {
                throw new RuntimeException("Two point are the same");
            }
            a = y1 - y2;
            b = x2 - x1;
            c = -a * x1 - b * y1;
        }

        Line(Point x, Point y) {
            this(x.x, x.y, y.x, y.y);
        }

        public Line perpendicular(Point p) {
            return new Line(p, new Point(p.x + a, p.y + b));
        }

        public Point intersect(Line l) {
            double d = a * l.b - b * l.a;
            if (d == 0) {
                throw new RuntimeException("Two lines are parallel");
            }

            return new Point((l.c * b - c * l.b) / d, (l.a * c - l.c * a) / d);
        }
    }

    static void check(Point a, Point b, ArrayList<Point> p, Point[] rec, int index) {
        for (int i = 0; i < 4; i++) {
            int m = (i + index) % 4;
            int j = (i + 1 + index) % 4;
            Point k = intersect(minus(b, a), minus(rec[m], rec[j]), minus(rec[m], a));
            if (k.x >= 0 && k.x <= 1 && k.y >= 0 && k.y <= 1) {
                Point val = new Point(k.x * minus(b, a).x, k.x * minus(b, a).y);
                p.add(add(val, a));
                // System.out.println(a + " " + b + " " + rec[i] + " " + rec[j]);
                // System.out.println(add(val, a));
            }
        }
    }

    static Point intersect(Point a, Point b, Point c) {
        double D = cross(a, b);
        if (D != 0) {
            return new Point(cross(c, b) / D, cross(a, c) / D);
        }
        return null;
    }

    static Point convert(Point a, double angle) {
        double x = a.x * cos(angle) - a.y * sin(angle);
        double y = a.x * sin(angle) + a.y * cos(angle);
        return new Point(x, y);
    }

    static Point minus(Point a, Point b) {
        return new Point(a.x - b.x, a.y - b.y);
    }

    static Point add(Point a, Point b) {
        return new Point(a.x + b.x, a.y + b.y);
    }

    static double cross(Point a, Point b) {
        return a.x * b.y - a.y * b.x;
    }

    static class Point {

        double x, y;

        Point(double x, double y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public String toString() {
            return "Point: " + x + " " + y;
        }
    }

    static class Scanner {

        BufferedReader br;
        StringTokenizer st;

        public Scanner() {
            // System.setOut(new PrintStream(new BufferedOutputStream(System.out), true));
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        public String next() {

            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (Exception e) {
                    throw new RuntimeException();
                }
            }
            return st.nextToken();
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public double nextDouble() {
            return Double.parseDouble(next());
        }

        public String nextLine() {
            st = null;
            try {
                return br.readLine();
            } catch (Exception e) {
                throw new RuntimeException();
            }
        }

        public boolean endLine() {
            try {
                String next = br.readLine();
                while (next != null && next.trim().isEmpty()) {
                    next = br.readLine();
                }
                if (next == null) {
                    return true;
                }
                st = new StringTokenizer(next);
                return st.hasMoreTokens();
            } catch (Exception e) {
                throw new RuntimeException();
            }
        }
    }
}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class C {

	static int[] nm;
	static int ans = 0;

	public static void main(String[] args) throws IOException {
		nm = readIntArray();
		if (nm[0] == 1) {
			ans = Math.max(nm[1] / 3, 1);
		} else if (nm[1] == 1) {
			ans = Math.max(nm[0] / 3, 1);
		} else {
			int a = (nm[1] / 2);
			int t = ((nm[0] / 2) + 1);
			int ans1 = a * t;
			if (nm[1] % 2 == 1)
				ans1 += t / 2;

			int a2 = (nm[0] / 2);
			int t2 = ((nm[1] / 2) + 1);
			int ans2 = a2 * t2;
			if (nm[0] % 2 == 1)
				ans2 += t2 / 2;

			ans = Math.min(ans1, ans2);
		}

		System.out.println(nm[0] * nm[1] - ans);
//		System.out.println(ans);
	}

	static int countDispositionDivisors(int[] d) {
		HashSet<Integer> div = new HashSet<Integer>();
		for (int i = 1; i <= d.length; i++) {
			for (int j = 0; j < d.length; j++) {
				if ((j + 1) % i == 0 && d[j] % i == 0) {
					div.add(j + 1);
					break;
				}
			}
		}

		return div.size();
	}

	static InputStreamReader isr = new InputStreamReader(System.in);
	static BufferedReader br = new BufferedReader(isr);

	static int[] readIntArray() throws IOException {
		String[] v = br.readLine().split(" ");
		int[] ans = new int[v.length];

		for (int i = 0; i < ans.length; i++) {
			ans[i] = Integer.valueOf(v[i]);
		}

		return ans;
	}

	static long[] readLongArray() throws IOException {
		String[] v = br.readLine().split(" ");
		long[] ans = new long[v.length];

		for (int i = 0; i < ans.length; i++) {
			ans[i] = Long.valueOf(v[i]);
		}

		return ans;
	}

	static <T> void print(List<T> v) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < v.size(); i++) {
			if (sb.length() > 0) {
				sb.append(' ');
			}

			sb.append(v.get(i));
		}

		System.out.println(sb);
	}
}

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class C {

	static int[] nm;
	static int ans = 0;

	public static void main(String[] args) throws IOException {
		nm = readIntArray();
		if (nm[0] == 1) {
			ans = nm[1] / 3 + (nm[1] % 3 == 0 ? 0 : 1);
		} else if (nm[1] == 1) {
			ans = nm[0] / 3 + (nm[0] % 3 == 0 ? 0 : 1);
		} else {
			int a = (nm[1] / 2);
			int t = ((nm[0] / 2) + 1);
			int ans1 = a * t;
			if (nm[1] % 2 == 1)
				ans1 += t / 2;

			int a2 = (nm[0] / 2);
			int t2 = ((nm[1] / 2) + 1);
			int ans2 = a2 * t2;
			if (nm[0] % 2 == 1)
				ans2 += t2 / 2;

			ans = Math.min(ans1, ans2);
		}

		System.out.println(nm[0] * nm[1] - ans);
//		System.out.println(ans);
	}

	static int countDispositionDivisors(int[] d) {
		HashSet<Integer> div = new HashSet<Integer>();
		for (int i = 1; i <= d.length; i++) {
			for (int j = 0; j < d.length; j++) {
				if ((j + 1) % i == 0 && d[j] % i == 0) {
					div.add(j + 1);
					break;
				}
			}
		}

		return div.size();
	}

	static InputStreamReader isr = new InputStreamReader(System.in);
	static BufferedReader br = new BufferedReader(isr);

	static int[] readIntArray() throws IOException {
		String[] v = br.readLine().split(" ");
		int[] ans = new int[v.length];

		for (int i = 0; i < ans.length; i++) {
			ans[i] = Integer.valueOf(v[i]);
		}

		return ans;
	}

	static long[] readLongArray() throws IOException {
		String[] v = br.readLine().split(" ");
		long[] ans = new long[v.length];

		for (int i = 0; i < ans.length; i++) {
			ans[i] = Long.valueOf(v[i]);
		}

		return ans;
	}

	static <T> void print(List<T> v) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < v.size(); i++) {
			if (sb.length() > 0) {
				sb.append(' ');
			}

			sb.append(v.get(i));
		}

		System.out.println(sb);
	}
}