written on 12 July 2023
Views : Loading...
Problem Link: https://cses.fi/problemset/task/1633
Your task is to count the number of ways to construct a sum $n$ by throwing a dice one or more times. Each throw produces an outcome between $1$ and $6$.
Solution:
int main()
{
int t;
cin >> t;
vector<int> dp(t + 1, 0);
dp[0] = 1;
for (int i = 1; i <= t; i++)
{
for (int j = 1; j <= 6; j++)
{
if (i - j < 0)
break;
dp[i] += dp[i - j];
dp[i] = dp[i] % mod;
}
}
cout << dp[t];
return 0;
}
Problem Link: https://cses.fi/problemset/task/1634/
Consider a money system consisting of $n$ coins. Each coin has a positive integer value. Your task is to produce a sum of money $x$ using the available coins in such a way that the number of coins is minimal.
Solution:
Recursion + Memoization:
#include<bits/stdc++.h>
using namespace std;
#define int long long int
#define double long double
#define endl '\n'
typedef long long ll;
const int MOD = 1000000007;
int n,x;
vector<int> coins;
int dp(int k, vector<int> &memo){
if (memo[k]!=0) return memo[k];
if (k<0) return INT_MAX;
if (k==0) return 0;
int ans = INT_MAX;
for (int i = 0; i < n; i++) {
if (coins[i]>k) continue;
ans=min(ans,dp(k-coins[i],memo)+1);
}
memo[k] = ans;
return ans;
}
signed main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>x;
coins.resize(n);
for (int i = 0; i < n; i++) {
cin>>coins[i];
}
vector<int> memo(x+1,0);
int ans = dp(x,memo);
if(ans!=INT_MAX){
cout<<ans<<endl;
} else {
cout<<-1<<endl;
}
return 0;
}
Tabulation:
int main()
{
int n,x;cin>>n>>x;
int coins[n];
for(int i=0;i<n;i++)cin>>coins[i];
vector<int>dp(x+1,1e9);
dp[0]=0;
for(int i=1;i<=x;i++){
for(auto c:coins){
if (i-c>=0){
dp[i] = min(dp[i],1+dp[i-c]);
}
}
}
if (dp[x]==1e9) cout<<-1;
else cout<<dp[x];
return 0;
}
Link: https://cses.fi/problemset/task/1635
Consider a money system consisting of $n$ coins. Each coin has a positive integer value. Your task is to calculate the number of distinct ways you can produce a money sum $x$ using the available coins.
Solution:
Recursion + Memoization (Gives TLE on 2 testcases)
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
const int MOD = 1000000007;
int n,x;
vector<int> coins;
int dp(int k, vector<int> &memo){
if (k<0) return 0;
if (k==0) return 1;
if (memo[k]!=0) return memo[k];
int ans = 0;
for (int i = 0; i < n; i++) {
ans += dp(k-coins[i],memo);
if (ans >= MOD)
ans = ans - MOD;
}
memo[k]=ans;
return ans;
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>x;
coins.resize(n);
for (int i = 0; i < n; i++) {
cin>>coins[i];
}
vector<int> memo(x+1,0);
cout<<dp(x,memo)<<endl;
return 0;
}
Tabulation:
int main()
{
int n,x;
cin>>n>>x;
int coins[n];
for(int i=0;i<n;i++) cin>>coins[i];
vector<int> dp(x+1,0);
dp[0]=1;
for(int i=1;i<x+1;i++){
for(auto c:coins){
if (i-c>=0){
dp[i]=(dp[i]+dp[i-c])%mod;
}
}
}
cout<<dp[x];
return 0;
}
Link: https://cses.fi/problemset/task/1636
Consider a money system consisting of $n$ coins. Each coin has a positive integer value. Your task is to calculate the number of distinct ordered ways you can produce a money sum $x$ using the available coins.
Solution:
Recursion + Memoization (Gives TLE) :
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
const int MOD = 1000000007;
int n,x;
vector<int> coins;
// There are 2 possibilities:
// 1) Use the jth coin.
// 2) Dont use the jth coin.
int dp(int i, int j,vector<vector<int>> &memo){
if (i<0) return 0;
if (i==0) return 1;
if (j==0) return 0;
if (memo[i][j]!=0) return memo[i][j];
int ans = 0;
ans+=dp(i-coins[j-1],j,memo); // use jth coin.
if (ans>=MOD) ans-=MOD;
ans+=dp(i,j-1,memo); // Dont use jth coin.
if (ans>=MOD) ans-=MOD;
return memo[i][j]=ans;
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>x;
coins.resize(n);
for (int i = 0; i < n; i++) {
cin>>coins[i];
}
vector<vector<int>> memo(x+1, vector<int> (n+1,0));
cout<<dp(x,n,memo)<<endl;
return 0;
}
Optimization using Tabulation (Gives TLE on 2 testcases):
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
const int MOD = 1000000007;
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n, x;
cin >> n >> x;
vector<int> coins(n);
for (int i = 0; i < n; i++) {
cin >> coins[i];
}
vector<vector<int>> dp(x + 1, vector<int>(n + 1, 0));
// Base case: If the target sum is 0, there is one way.
for (int j = 0; j <= n; j++) {
dp[0][j] = 1;
}
for (int i = 1; i <= x; i++) {
for (int j = 1; j <= n; j++) {
dp[i][j] = dp[i][j - 1]; // Exclude the jth coin.
if (i - coins[j - 1] >= 0) {
dp[i][j] += dp[i - coins[j - 1]][j]; // Include the jth coin.
dp[i][j] %= MOD;
}
}
}
cout << dp[x][n] << endl;
return 0;
}
Final Optimization:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
const int MOD = 1000000007;
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n, x;
cin >> n >> x;
vector<int> coins(n);
for (int i = 0; i < n; i++) {
cin >> coins[i];
}
vector<int> dp(x + 1, 0);
dp[0] = 1;
for (int j = 1; j <= n; j++) {
for (int i = 1; i <= x; i++) {
if (i - coins[j - 1] >= 0) {
dp[i] += dp[i - coins[j - 1]];
dp[i] %= MOD;
}
}
}
cout << dp[x] << endl;
return 0;
}
Link: https://cses.fi/problemset/task/1638
Consider an $n \times n$ grid whose squares may have traps. It is not allowed to move to a square with a trap.
Solution:
$$\text{dp}[x][y] = \begin{cases} \text{dp}[x-1][y] + \text{dp}[x][y-1] & \text{if $(x, y)$ is not a trap} \ 0 & \text{if $(x, y)$ is a trap} \end{cases}$$
Recursion + Memoization (Gives TLE on some Testcases):
#include<bits/stdc++.h>
using namespace std;
#define int long long int
#define endl '\n'
const int MOD = 1000000007;
int n;
// I can either come from left or from up.
int dp(int i,int j,vector<vector<char>> &grid,vector<vector<int>>&memo){
if (i<0 || i>=n ||j<0 || j>=n) return 0;
if (grid[i][j]=='*'){
return 0;
}
if (i==0 && j==0) return 1;
if (memo[i][j]!=0) return memo[i][j];
return memo[i][j] = (dp(i-1,j,grid,memo) + dp(i,j-1,grid,memo))%MOD;
}
signed main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
vector<vector<char> > grid(n,vector<char> (n) );
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin>>grid[i][j];
}
}
vector<vector<int>> memo(n,vector<int> (n+1,0));
cout<<dp(n-1,n-1,grid,memo);
return 0;
}
Tabulation:
#include<bits/stdc++.h>
using namespace std;
#define int long long int
#define endl '\n'
const int MOD = 1000000007;
int n;
int dp(vector<vector<char>>& grid, vector<vector<int>>& memo) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '*') {
memo[i][j] = 0;
} else {
if (i == 0 && j == 0) {
memo[i][j] = 1;
} else {
int from_left = (j - 1 >= 0) ? memo[i][j - 1] : 0;
int from_up = (i - 1 >= 0) ? memo[i - 1][j] : 0;
memo[i][j] = (from_left + from_up) % MOD;
}
}
}
}
return memo[n - 1][n - 1];
}
signed main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
vector<vector<char>> grid(n, vector<char>(n));
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> grid[i][j];
}
}
vector<vector<int>> memo(n, vector<int>(n, 0));
cout << dp(grid, memo);
return 0;
}
Link: https://cses.fi/problemset/task/1158
You are in a book shop which sells $n$ different books. You know the price and number of pages of each book. You have decided that the total price of your purchases will be at most $x$. What is the maximum number of pages you can buy? You can buy each book at most once.
Solution:
Recursion + Memoization (Gives TLE probably because of function calls):
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
const int MOD = 1000000007;
int memo[1001][100001]{};
int prices[1001], pages[1001]{};
int n,x;
// For each book I have 2 choices:
// 1. Buy it.
// 2. Dont buy it.
// i is the amount of money i have, j is the first j books.
int dp(int currentBook, int remainingMoney) {
// if no books left or no money left.
if (currentBook==0 || remainingMoney == 0) return 0;
if (memo[currentBook][remainingMoney] != 0) {
return memo[currentBook][remainingMoney];
}
// Dont buy the current book
int ans = dp(currentBook-1,remainingMoney);
if (remainingMoney>=prices[currentBook]){
ans = max(ans, dp(currentBook-1,remainingMoney-prices[currentBook]) + pages[currentBook] );
}
return memo[currentBook][remainingMoney] = ans;
}
signed main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,x;
cin>>n>>x;
for (int i = 1; i < n+1; i++) {
cin>>prices[i];
}
for (int i = 1; i < n+1; i++) {
cin>>pages[i];
}
cout<<dp(n,x);
return 0;
}
Tabulation:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
const int MOD = 1000000007;
int dp[1001][100001]{};
int prices[1001], pages[1001]{};
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int n, x;
cin >> n >> x;
for (int i = 1; i <= n; i++) {
cin >> prices[i];
}
for (int i = 1; i <= n; i++) {
cin >> pages[i];
}
for(int i=1;i<n+1;i++){
for(int j=0;j<x+1;j++){
dp[i][j]=dp[i-1][j]; // Dont buy
if(j-prices[i]>=0){
dp[i][j] = max ( dp[i][j], dp[i-1][j-prices[i]]+pages[i] ); // Buy it.
}
}
}
cout << dp[n][x];
return 0;
}
Link: https://cses.fi/problemset/task/1746
You know that an array has n integers between 1 and m, and the absolute difference between two adjacent values is at most 1. Given a description of the array where some values may be unknown, your task is to count the number of arrays that match the description.
Solution:
Recursion + Memoization (Gives TLE for some testcases):
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1000000007;
int n, m;
vector<int> a;
int memo[101][100001]{};
// At every 0 try all the possible values and count.
int dp(int i, int prev) {
if (memo[prev][i] != 0) return memo[prev][i];
if (i == n) return 1;
if (a[i] != 0 && abs(prev - a[i]) > 1) return 0;
if (a[i] != 0) return dp(i + 1, a[i]);
int count = 0;
for (int k = prev - 1; k <= prev + 1; k++) {
if (k >= 1 && k <= m) {
count += dp(i + 1, k);
if (count >= MOD) count -= MOD;
}
}
return memo[prev][i] = count;
}
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n >> m;
a.resize(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
int ans = 0;
if (a[0] == 0) {
for (int i = 1; i <= m; i++) {
ans += dp(1, i);
if (ans > MOD) ans -= MOD;
}
} else {
ans = dp(1, a[0]);
}
cout << ans;
return 0;
}
Tabulation:
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1000000007;
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
int n, m;
cin >> n >> m;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
dp[a[0]][1] = (a[0] == 0) ? 1 : 0;
for (int i = 1; i <= m; i++) {
if (a[0] == 0 || a[0] == i) {
dp[i][1] = 1;
}
}
for (int i = 2; i <= n; i++) {
for (int prev = 1; prev <= m; prev++) {
if (a[i - 1] == 0 || a[i - 1] == prev) {
for (int k = prev - 1; k <= prev + 1; k++) {
if (k >= 1 && k <= m) {
dp[prev][i] = (dp[prev][i] + dp[k][i - 1]) % MOD;
}
}
}
}
}
int ans = 0;
for (int i = 1; i <= m; i++) {
if (a[n - 1] == 0 || a[n - 1] == i) {
ans = (ans + dp[i][n]) % MOD;
}
}
cout << ans;
return 0;
}
Link: https://cses.fi/problemset/task/1639
The edit distance between two strings is the minimum number of operations required to transform one string into the other.
The allowed operations are:
Solution:
This is a standard DP problem.
Problems like Minimum ASCII Delete Sum for Two Strings, Longest Common Subsequence (LCS), and many more are special cases of this problem.
Initialize a 2D DP table of size $(n+1) \times (m+1)$. The extra 1 in each dimension is for accommodating the empty string as a base case.
Initialize the first row and first column of the DP table.
Iterate over the DP table row by row and column by column, starting from $DP[1][1]$. For each position $DP[i][j]$, consider the characters $s1[i-1]$ and $s2[j-1]$:
Once the DP table is filled, the minimum edit distance will be stored in $DP[n][m]$.
int main()
{
string a,b;
cin>>a>>b;
int na = a.length();
int nb = b.length();
vector < vector<int> > dp(na+1,vector<int>(nb+1,1e9));
dp[0][0]=0;
for(int i=0;i<na+1;i++){
for(int j=0;j<nb+1;j++){
if (i>0) dp[i][j]=min(dp[i][j],1+dp[i-1][j]);
if (j>0) dp[i][j]=min(dp[i][j],1+dp[i][j-1]);
if (j>0 and i>0){
if (a[i-1]==b[j-1]) dp[i][j]=min(dp[i][j],dp[i-1][j-1]);
else{
dp[i][j]=min(dp[i][j],1+dp[i-1][j-1]);
}
}
}
}
cout<<dp[na][nb]<<endl;
return 0;
}
Link: https://cses.fi/problemset/task/1744/
Given an $a \times b$ rectangle, your task is to cut it into squares. On each move, you can select a rectangle and cut it into two rectangles in such a way that all side lengths remain integers. What is the minimum possible number of moves?
Solution:
int main()
{
int a,b;
cin>>a>>b;
vector< vector<int> > dp(a+1 , vector<int> (b+1,1e9));
for(int i=0;i<a+1;i++){
for(int j=0;j<b+1;j++){
if(i==j){
dp[i][j]=0;
}
else{
for(int k=1;k<i;k++){
dp[i][j]=min(dp[i][j],dp[i-k][j]+dp[k][j]+1);
}
for(int k=1;k<j;k++){
dp[i][j]=min(dp[i][j],dp[i][j-k]+dp[i][k]+1);
}
}
}
}
cout<<dp[a][b]<<endl;
return 0;
}
Link: https://cses.fi/problemset/task/1745
You have $n$ coins with certain values. Your task is to find all money sums you can create using these coins.
Solution:
This problem can be seen as a variation of the subset sum problem, which is a well-known dynamic programming problem.
In the subset sum problem, we are given a set of integers, and the goal is to determine if there exists a subset of the integers that sums up to a given target value. The solution to the subset sum problem can be modified to solve the problem of finding all money sums.
Let's first find $S$, which is the sum of all coin values. This is the maximum sum we can get.
Initialize a boolean array $dp$ of size $(S+1)$, where $S$ is the sum of all coin values.
Set all elements of $dp$ to false initially.
Set $dp[0]$ to true, indicating that we can always make a sum of 0 using an empty set.
Iterate over each coin value from 1 to $n$ (assuming the coins are stored in an array called $coins$).
After completing the iterations, all indices $j$ for which $dp[j]$ is true represent the money sums that can be created using the given coins.
signed main()
{
int n; cin>>n;
int coin_sum = 0;
vector<int> coins;
for(int i=0;i<n;i++){
int c;cin>>c;
coins.emplace_back(c);
coin_sum+=c;
}
vector<bool>dp(coin_sum+1,false);
dp[0]=true;
for(int coin: coins){
for(int i=coin_sum;i>=coin;i--){
if (dp[i-coin]) dp[i]=true;
}
}
int count = 0;
for(int i=1;i<coin_sum+1;i++){
if (dp[i]) count++;
}
cout<<count<<endl;
for(int i=1;i<coin_sum+1;i++){
if (dp[i]) cout<<i<<" ";
}
cout<<endl;
return 0;
}
Link: https://cses.fi/problemset/task/1097/
There is a list of $n$ numbers and two players who move alternately. On each move, a player removes either the first or last number from the list, and their score increases by that number. Both players try to maximize their scores.
Solution:
This is a well-known and frequently studied problem in game theory and dynamic programming.
It can be categorized as a variation of the classic game called "Nim," which involves two players taking turns removing objects from distinct piles.
It is an advanced problem involving game theory and optimal decision-making in competitive scenarios.
A solution video by MIT can be found here.
For every position $(i,j)$, we have some choices:
Memoize the solution.
ll score(vector<vector<ll>> &dp, ll l[], int i, int j)
{
if (dp[i][j] != 1e9) return dp[i][j];
if (i == j)
{
dp[i][j] = l[i];
return l[i];
}
if (i + 1 == j)
{
dp[i][j] = max(l[i], l[j]);
return max(l[i], l[j]);
}
dp[i][j] = max(
min(score(dp, l, i + 1, j - 1), score(dp, l, i + 2, j)) + l[i],
min(score(dp, l, i, j - 2), score(dp, l, i + 1, j - 1)) + l[j]);
return dp[i][j];
}
// Code
int main()
{
fast;
ll n;
cin >> n;
ll l[n];
for (int i = 0; i < n; i++)
cin >> l[i];
vector<vector< ll > > dp(n, vector<ll>(n, 1e9));
cout << score(dp, l, 0, n - 1);
return 0;
}
Link: https://cses.fi/problemset/task/1093
Your task is to count the number of ways numbers $1, 2, \ldots, n$ can be divided into two sets of equal sum.
Solution:
int main()
{
int n;cin>>n;
ll x = (1ll)*n*(n+1);
if (x%4!=0) cout<<"0"<<endl;
else{
x = x/4;
vector< vector<ll> > dp(n,vector<ll>(x+1,0));
dp[0][0]=1;
for(ll i=1;i<n;i++){
for(ll j=0;j<=x;j++){
if (j>=i){
dp[i][j] += dp[i-1][j-i];
}
dp[i][j]=(dp[i][j]+dp[i-1][j])%mod;
}
}
cout<<dp[n-1][x]<<endl;
}
return 0;
}
Link: https://www.interviewbit.com/problems/longest-palindromic-subsequence/
Solution:
MIT Solution Video: https://youtu.be/Tw1k46ywN6E?t=317
Let $DP[i][j]$ be the length of the longest palindromic subsequence in the range $A[i:j]$.
For every $i$ and $j$ pair, we have 2 choices:
Memoize it for the pair $(i,j)$, then the final complexity is $O(n^2)$.
or
class Solution:
# @param A : string
# @return an integer
def solve(self, A):
n = len(A)
B = A[::-1]
dp = [[0 for i in range(n+1)] for j in range(n+1)]
for i in range(n+1):
for j in range(n+1):
if i==0 or j==0:
dp[i][j]=0
else:
if A[i-1]==B[j-1]:
dp[i][j]=1+dp[i-1][j-1]
else:
dp[i][j]=max(dp[i-1][j],dp[i][j-1])
return dp[n][n]
Link: https://cses.fi/problemset/task/1140/
int binary_search(vector<tuple<int, int, int>> &projects, int &target)
{
int low = 0, high = projects.size() - 1, result = -1;
while (low <= high)
{
int mid = low + (high - low) / 2;
if (get<1>(projects[mid]) < target)
{
result = mid;
low = mid + 1;
}
else
high = mid - 1;
}
return result;
}
signed main()
{
int n;
cin >> n;
vector<tuple<int, int, int>> projects;
for (int i = 0; i < n; i++)
{
int a, b, c;
cin >> a >> b >> c;
projects.emplace_back(make_tuple(a, b, c));
}
sort(projects.begin(), projects.end(), [](tuple<int, int, int> &a, tuple<int, int, int> &b)
{ return get<1>(a) < get<1>(b); });
vector<int> dp(n + 1, 0);
for (int i = 1; i < n + 1; i++)
{
int j = binary_search(projects, get<0>(projects[i - 1]));
dp[i] = max(get<2>(projects[i - 1]) + dp[j + 1], dp[i - 1]);
}
cout << dp[n];
return 0;
}
Link: https://cses.fi/problemset/task/1653
signed main()
{
int n, x;
cin >> n >> x;
vector<int> weights(n, 0);
for (int i = 0; i < n; i++)
cin >> weights[i];
int INF = 1e9;
vector< pair<int,int> > dp(1<<n,{INF,INF});
dp[0] = {0,INF};
for(int mask = 1;mask<(1<<n);mask++){
pair<int,int> best = {INF,INF};
for(int i=0;i<n;i++){
pair<int,int> cur = dp[mask ^ (1<<i)];
if (cur.second + weights[i]>x){
cur = {cur.first+1,weights[i]};
}
else{
cur.second+=weights[i];
}
best = min(best,cur);
}
dp[mask] = best;
}
cout<< dp[(1<<n)-1].first <<endl;
return 0;
}
Link: https://cses.fi/problemset/task/1690
There are n cities and m flight connections between them. You want to travel from Syrjälä to Lehmälä so that you visit each city exactly once. How many possible routes are there?
Solution:
signed main()
{
int n, m;
cin >> n >> m;
vector<vector<int>> dp((1 << n), vector<int>(n, 0));
vector<vector<int>> graph(n, vector<int>());
for (int i = 0; i < m; i++)
{
int u, v;
cin >> u >> v;
graph[--v].emplace_back(--u);
}
dp[1][0] = 1;
for (int mask = 2; mask < (1 << n); mask++){
if ((mask & 1) == 0) continue;
if (((mask & (1 << (n - 1)))) && (mask != ((1 << n) - 1))) continue;
for (int i = 0; i < n; i++){
if ((mask & (1 << i)) == 0) continue;
int new_mask = mask ^ (1 << i);
for (int prev_city : graph[i]) {
if (mask & (1 << prev_city)) {
dp[mask][i] += dp[new_mask][prev_city];
dp[mask][i] %= mod;
}
}
}
}
cout << dp[(1 << n) - 1][n - 1] << endl;
return 0;
}
Link: https://cses.fi/problemset/task/2181/
Your task is to count the number of ways you can fill an n×m grid using 1×2 and 2×1 tiles.
Solution: This is a problem of DP on broken profiles.
We'll process the grid cells column-by-column, row-by-row. Let $i$ and $j$ denote the row and column of the current cell we are considering, and $\texttt{dp}[i][j][mask]$ be the number of ways to tile the grid so that:
The answer will be $\texttt{dp}[N - 1][M - 1][0]$.
We now have three cases when calculating $\texttt{dp}[i][j][mask]$:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int ll
#define endl '\n'
const ll MOD = 1000000007;
int dp[1 << 10][2];
signed main()
{
int n, m;
cin >> n >> m;
dp[0][0] = 1;
for (int j = 0; j < m; j++)
for (int i = 0; i < n; i++)
{
for (int mask = 0; mask < (1 << n); mask++)
{
dp[mask][1] = dp[mask ^ (1 << i)][0];
if (i && !(mask & (1 << i)) && !(mask & (1 << (i - 1))))
dp[mask][1] += dp[mask ^ (1 << (i - 1))][0];
if (dp[mask][1] >= MOD)
dp[mask][1] -= MOD;
}
for (int mask = 0; mask < (1 << n); mask++)
dp[mask][0] = dp[mask][1];
}
cout << dp[0][0] << '\n';
return 0;
}