Dynamic Programming in Game Theory for Competitive Programming (original) (raw)

Last Updated : 23 Jul, 2025

In the fast-paced world of competitive programming, mastering dynamic programming in game theory is the key to solving complex strategic challenges. This article explores how **dynamic programming in game theory can enhance your problem-solving skills and strategic insights, giving you a competitive edge. Whether you're a seasoned coder or a newcomer, this article uncover the power of **Dynamic Programming in Game Theory for Competitive Programming.

Problem Identification of Dynamic Programming in Game Theory:

In these type of problems you are basically given 2 players(first and second), set of operations, and a win condition. Given that both the players play optimally and they take turn one after the other we have to determine the winner of the game.

Winning State and Loosing State:

If a player is standing at a particular state ****'S'** and it can send its opponent to one of the K different states T1, T2...TK then:

Obviously, in order to win the player will try to send its opponent to any loosing state and if it is not possible to do so then that player itself looses the game. follow the below image for better understanding.

Winning-and-Loosing-State-for-a-playerdrawio

Lets's take a look over some problems to understand this concept more thoroughly:

**Problem 1:

Two players (First and Second) are playing a game on a 2-D plane in which a Token has been place at coordinate ****(0,0)**. Players can perform two types of operations on the token:

  1. Increase the **X coordinate by exactly **K distance
  2. Increase the Y coordinate by exactly K distance

You are given an integer **D, In order to perform above operations the player must ensure that the token stays within Euclidean distance **D from ****(0,0)** i.e. after each operation, **X 2 + Y 2 <= D 2 where **X and **Y are the coordinates of the token.

Given the value of **K and **D, determine the Winner of the Game assuming both the players play optimally and First player plays first.

**Example:

**Input: D=2, K=1
**Output: Second
**Explanation: First player moves token from (0,0) to (0,1)
Second player moves token from (0,1) to (0,2)
Now, whatever move First player makes, the distance will exceed the Euclidean Distance

**Input: D=5, K=2
**Output: Second

**Approach:

Let DP[i][j] denote whether the coordinate ****(i,j)** are a winning state or a loosing state for any player that is:

**Recurrence Relation:

On the basis of given operations we can either increment x coordinate by **K or **y coordinate by **K, hence **DP[i][j] depends on two values:

Condition for (i, j) to be winning state i.e. DP[i][j] = 1 :

Condition for (i, j) to be loosing state i.e. DP[i][j] = 0:

Below is the implementation of the above approach:

C++ `

#include <bits/stdc++.h> #define ll long long using namespace std; ll d, k; ll findWinner(ll i, ll j, vector<vector >& dp) { // ans varible determines whether one of dp[i+k][j] or // dp[i][j+k] is 0 or not ll ans = 1; if (dp[i][j] != -1) return dp[i][j]; // x and y stores the possible eucledian distances from // current coordinates i and j ll x = (i + k) * (i + k) + j * j; ll y = (i) * (i) + (j + k) * (j + k); // if x is valid eucledian distance if (x <= d * d) { ans = ans & (findWinner(i + k, j, dp)); } // if y is valid eucledian distance if (y <= d * d) { ans = ans & (findWinner(i, j + k, dp)); } // ans=0 means current state is a winning state if (ans == 0) return dp[i][j] = 1; return dp[i][j] = 0; } // driver code int main() { d = 5; k = 2;

vector<vector<int> > dp(d * d + 1,
                        vector<int>(d * d + 1, -1));
int ans = findWinner(0, 0, dp);
if (ans == 1) {
    cout << "First player wins";
}
else
    cout << "Second player wins";
cout << endl;

}

Java

// Java Implementation : import java.util.Arrays;

public class GameWinner { private static int d, k;

public static int findWinner(int i, int j, int[][] dp) {
    int ans = 1;
    if (dp[i][j] != -1) {
        return dp[i][j];
    }
    
    int x = (i + k) * (i + k) + j * j;
    int y = i * i + (j + k) * (j + k);
    
    if (x <= d * d) {
        ans &= findWinner(i + k, j, dp);
    }
    
    if (y <= d * d) {
        ans &= findWinner(i, j + k, dp);
    }
    
    if (ans == 0) {
        return dp[i][j] = 1;
    }
    
    return dp[i][j] = 0;
}

public static void main(String[] args) {
    d = 5;
    k = 2;
    
    int[][] dp = new int[d * d + 1][d * d + 1];
    for (int[] row : dp) {
        Arrays.fill(row, -1);
    }
    
    int ans = findWinner(0, 0, dp);
    if (ans == 1) {
        System.out.println("First player wins");
    } else {
        System.out.println("Second player wins");
    }
}

} // This code is contributed by Sakshi

Python3

Function to find the winner

def findWinner(i, j, dp): # ans variable determines whether one of dp[i+k][j] or # dp[i][j+k] is 0 or not ans = 1

if dp[i][j] != -1:
    return dp[i][j]

# x and y store the possible Euclidean distances from
# current coordinates i and j
x = (i + k) * (i + k) + j * j
y = i * i + (j + k) * (j + k)

# If x is a valid Euclidean distance
if x <= d * d:
    ans = ans & findWinner(i + k, j, dp)

# If y is a valid Euclidean distance
if y <= d * d:
    ans = ans & findWinner(i, j + k, dp)

# ans=0 means the current state is a winning state
if ans == 0:
    dp[i][j] = 1
else:
    dp[i][j] = 0

return dp[i][j]

Driver code

d = 5 k = 2

dp = [[-1 for _ in range(d * d + 1)] for _ in range(d * d + 1)] ans = findWinner(0, 0, dp)

if ans == 1: print("First player wins") else: print("Second player wins")

C#

// C# program for the above approach using System;

public class GFG { static long d, k;

static long FindWinner(long i, long j, long[, ] dp)
{
    // ans variable determines whether one of dp[i+k,j]
    // or dp[i,j+k] is 0 or not
    long ans = 1;

    if (dp[i, j] != -1)
        return dp[i, j];

    // x and y store the possible Euclidean distances
    // from current coordinates i and j
    long x = (i + k) * (i + k) + j * j;
    long y = i * i + (j + k) * (j + k);

    // if x is a valid Euclidean distance
    if (x <= d * d) {
        ans = ans & (FindWinner(i + k, j, dp));
    }

    // if y is a valid Euclidean distance
    if (y <= d * d) {
        ans = ans & (FindWinner(i, j + k, dp));
    }

    // ans=0 means the current state is a winning state
    if (ans == 0)
        return dp[i, j] = 1;

    return dp[i, j] = 0;
}

static void Main()
{
    d = 5;
    k = 2;

    // Initialize a 2D array to store the intermediate
    // results
    long[, ] dp = new long[d * d + 1, d * d + 1];

    // Initialize the array with -1 (indicating that the
    // result is not calculated yet)
    for (int i = 0; i <= d * d; i++) {
        for (int j = 0; j <= d * d; j++) {
            dp[i, j] = -1;
        }
    }

    // Call the function to find the winner and convert
    // the result to an integer
    int ans = Convert.ToInt32(FindWinner(0, 0, dp));

    // Output the result based on the calculated winner
    if (ans == 1) {
        Console.WriteLine("First player wins");
    }
    else {
        Console.WriteLine("Second player wins");
    }
}

}

// This code is contributed by Susobhan Akhuli

JavaScript

// Function to find the winner function findWinner(i, j, dp) {

// ans varible determines whether one of dp[i+k][j] or
// dp[i][j+k] is 0 or not
let ans = 1;
if (dp[i][j] !== -1)
    return dp[i][j];

// x and y stores the possible eucledian distances from
// current coordinates i and j
let x = (i + k) * (i + k) + j * j;
let y = i * i + (j + k) * (j + k);

// if x is valid eucledian distance
if (x <= d * d)
    ans = ans & findWinner(i + k, j, dp);

// if y is valid eucledian distance
if (y <= d * d)
    ans = ans & findWinner(i, j + k, dp);

// ans=0 means current state is a winning state
if (ans === 0)
    return dp[i][j] = 1;
return dp[i][j] = 0;

}

// driver code

let d = 5; let k = 2; let dp = new Array(d * d + 1).fill().map(() => new Array(d * d + 1).fill(-1)); let ans = findWinner(0, 0, dp); if (ans === 1) { console.log("First player wins"); } else { console.log("Second player wins"); }

`

Problem 2:

Two players (First and Second) are playing a game on array **arr[] of size **N. The players are building a sequence together, initially the sequence is empty. In one turn the player can perform either of the following operations:

The rule is that the sequence must always be strictly increasing, the winner is the player that makes the last move. The task is to determine the winner.

**Example:

**Input: arr=[5, 4, 5]
**Output: First Player Wins
**Explanation: After the first player append 5 into the sequence the array would look like either [4,5] or [5,4] and second player won't be able to make any move.

**Input: arr=[5, 8, 2, 1, 10, 9]
**Output: Second Player Wins
**Explanation: For any element the first player append to the sequence, the second player can always append a strictly greater elements.

**Solution:

Let **DP[L][R] denote whether the subarray from **L to **R is a Loosing state or a Winning state for any player that is:

**Recurrence Relation:

As the operations allow a player to only remove from the rightmost end or the leftmost end therefore DP[L][R] will depend on two ranges based on two conditions:

Condition for (L, R) to be winning state i.e. DP[L][R] = 1 :

Condition for (L, R) to be loosing state i.e. DP[L][R] = 0:

Below is the implementation of the above approach:

C++ `

#include <bits/stdc++.h> #define ll long long using namespace std; // function to determine the winner ll findWinner(ll l, ll r, ll last, vector& arr, vector<vector >& dp) { // if l>r means current state is a loosing state as we can // not make any move if (l > r) { return 0; } // x = leftmost element of current subarray l to r ll x = arr[l]; // y = rightmost element of current subarray l to r ll y = arr[r]; // ans variable is used to know whether either of the // next transitions are loosing state or not ll ans = 1; if (dp[l][r] != -1) return dp[l][r]; // if we can take the leftmost element in the sequence if (x > last) { ans = ans & (findWinner(l + 1, r, x, arr, dp)); } // if we can take rightmost element in the sequence if (y > last) { ans = ans & (findWinner(l, r - 1, y, arr, dp)); } // ans=0 means we found a loosing state in some next // transistions hence current state is winning if (ans == 0) return dp[l][r] = 1; else return dp[l][r] = 0; } // driver code int main() { vector arr = { 5, 8, 2, 1, 10, 9 }; ll n = arr.size(); // dp of size n*n to store all the L to R ranges vector<vector > dp(n, vector(n, -1)); // recursive call ll ans = findWinner(0, n - 1, INT_MIN, arr, dp); if (ans) { cout << "First player wins"; } else cout << "Second player wins"; }

Java

// Java program for the above approach import java.util.Arrays;

public class GFG {

// Function to determine the winner
static long findWinner(int l, int r, long last,
                       long[] arr, long[][] dp)
{
    // If l > r means the current state is a losing
    // state as we cannot make any move
    if (l > r) {
        return 0;
    }

    // x = leftmost element of the current subarray l to
    // r
    long x = arr[l];
    // y = rightmost element of the current subarray l
    // to r
    long y = arr[r];

    // ans variable is used to know whether either of
    // the next transitions are losing states or not
    long ans = 1;
    if (dp[l][r] != -1)
        return dp[l][r];

    // If we can take the leftmost element in the
    // sequence
    if (x > last) {
        ans &= (findWinner(l + 1, r, x, arr, dp));
    }

    // If we can take the rightmost element in the
    // sequence
    if (y > last) {
        ans &= (findWinner(l, r - 1, y, arr, dp));
    }

    // ans = 0 means we found a losing state in some
    // next transitions, hence the current state is
    // winning
    if (ans == 0)
        return dp[l][r] = 1;
    else
        return dp[l][r] = 0;
}

// Driver code
public static void main(String[] args)
{
    long[] arr = { 5, 8, 2, 1, 10, 9 };
    int n = arr.length;

    // DP array of size n*n to store all the L to R
    // ranges
    long[][] dp = new long[n][n];
    for (long[] row : dp)
        Arrays.fill(row, -1);

    // Recursive call
    long ans = findWinner(0, n - 1, Integer.MIN_VALUE,
                          arr, dp);

    if (ans == 1) {
        System.out.println("First player wins");
    }
    else {
        System.out.println("Second player wins");
    }
}

}

// This code is contributed by Susobhan Akhuli

Python3

Python Implementation

def find_winner(l, r, last, arr, dp): if l > r: return 0 x = arr[l] y = arr[r] ans = 1 if dp[l][r] != -1: return dp[l][r] if x > last: ans = ans & find_winner(l + 1, r, x, arr, dp) if y > last: ans = ans & find_winner(l, r - 1, y, arr, dp) if ans == 0: dp[l][r] = 1

else:
    dp[l][r] = 0
return dp[l][r]

arr = [5, 8, 2, 1, 10, 9] n = len(arr) dp = [[-1] * n for _ in range(n)] ans = find_winner(0, n - 1, float('-inf'), arr, dp) if ans: print("First player wins") else: print("Second player wins")

This code is contributed by Tapesh(tapeshdua420)

C#

// C# program for the above approach using System; using System.Collections.Generic;

public class GFG { static long FindWinner(long l, long r, long last, List arr, List<List > dp) { // If l > r means the current state is a losing // state as we cannot make any move if (l > r) { return 0; }

    // x = leftmost element of the current subarray l to
    // r
    long x = arr[(int)l];
    // y = rightmost element of the current subarray l
    // to r
    long y = arr[(int)r];
    // ans variable is used to know whether either of
    // the next transitions is a losing state or not
    long ans = 1;

    if (dp[(int)l][(int)r] != -1)
        return dp[(int)l][(int)r];

    // If we can take the leftmost element in the
    // sequence
    if (x > last) {
        ans = ans & (FindWinner(l + 1, r, x, arr, dp));
    }

    // If we can take the rightmost element in the
    // sequence
    if (y > last) {
        ans = ans & (FindWinner(l, r - 1, y, arr, dp));
    }

    // ans = 0 means we found a losing state in some
    // next transitions hence the current state is
    // winning
    if (ans == 0)
        return dp[(int)l][(int)r] = 1;
    else
        return dp[(int)l][(int)r] = 0;
}

static void Main()
{
    List<long> arr
        = new List<long>{ 5, 8, 2, 1, 10, 9 };
    long n = arr.Count;

    // dp of size n*n to store all the L to R ranges
    List<List<long> > dp = new List<List<long> >();
    for (int i = 0; i < n; i++) {
        dp.Add(new List<long>(new long[n]));
        for (int j = 0; j < n; j++) {
            dp[i][j] = -1;
        }
    }

    // Recursive call
    long ans
        = FindWinner(0, n - 1, int.MinValue, arr, dp);

    if (ans == 1) {
        Console.WriteLine("First player wins");
    }
    else {
        Console.WriteLine("Second player wins");
    }
}

}

// This code is contributed by Susobhan Akhuli

JavaScript

// function to determine the winner function findWinner(l, r, last, arr, dp) { // if l>r means current state is a loosing state as we can // not make any move if (l > r) { return 0; } // x = leftmost element of current subarray l to r let x = arr[l]; // y = rightmost element of current subarray l to r let y = arr[r]; // ans variable is used to know whether either of the // next transitions are loosing state or not let ans = 1; if (dp[l][r] !== -1) { return dp[l][r]; } // if we can take the leftmost element in the sequence if (x > last) { ans = ans & (findWinner(l + 1, r, x, arr, dp)); } // if we can take rightmost element in the sequence if (y > last) { ans = ans & (findWinner(l, r - 1, y, arr, dp)); } // ans=0 means we found a loosing state in some next // transitions hence current state is winning if (ans === 0) { return dp[l][r] = 1; } else { return dp[l][r] = 0; } }

// driver code function main() { let arr = [5, 8, 2, 1, 10, 9]; let n = arr.length; // dp of size n*n to store all the L to R ranges let dp = Array.from({ length: n }, () => Array(n).fill(-1)); // recursive call let ans = findWinner(0, n - 1, Number.MIN_SAFE_INTEGER, arr, dp); if (ans) { console.log("First player wins"); } else { console.log("Second player wins"); } }

main();

`

Practice Problems on Dynamic Programming in Game Theory:

Optimal Strategy for a Game
Optimal Strategy for a Game | Set 2
Optimal Strategy for a Game | Set 3
Optimal Strategy for the Divisor game using Dynamic Programming
Game of N stones where each player can remove 1, 3 or 4
Find the player who will win by choosing a number in range [1, K] with sum total N
Find the winner of the game with N piles of boxes
Coin game of two corners (Greedy Approach)