simple java 2d DP solution with detailed explaination and comments
DP states:\nlong[][] dp[n][rollMax.length +1]\n When j < rollMax.length: dp[i][j] records number of valid combination with i number of rolls and dice end with j
#### **DP states**:\nlong[][] dp[n][rollMax.length **+1**]\n* When j < rollMax.length: dp[i][j] records number of valid combination with i number of rolls and dice end with j+1 value.\n* When j ==rollMax.length: dp[i][rollMax.length] records total number of valid combinations with i number of rolls. \n\t\t\n#### **Initialization**:\n```\n        for(int i = 0; i < rollMax.length; ++i) {\n            dp[1][i] = 1;\n        }\n        \n        // when there is 0 rolls there is only one combination (empty)\n        dp[0][rollMax.length] = 1;   \n        // total number with 1 roll; \n        dp[1][rollMax.length] = rollMax.length; \n```\n#### **Induction rule:**\n```\nfor(int i = 2; i <= n; ++i) {\n          for(int j = 0; j < rollMax.length; ++j) {\n                // based on max allowed occurence to calculate the possible combinations end with j + 1\n                for(int k = 1; k <= rollMax[j] && i >= k; ++k) {\n\t\t\t\t    // how many valid combination will end with k succsccive rolls with value of j + 1? \n\t\t\t\t\t// dp[i-k][rollMax.length] - dp[i-k][j]  is number of combinations that i-k rolls have that does not end with j+1\n                    // the rest k rolls with have consistent value of j + 1\n\t\t            totalEndWithCurNum += (dp[i-k][rollMax.length] - dp[i-k][j] + this.mod) % this.mod;\n                    totalEndWithCurNum %= this.mod;\n                }\n                \n                total += totalEndWithCurNum; \n                total %= this.mod;\n                dp[i][j] = totalEndWithCurNum;\n            }\n            \n            dp[i][rollMax.length] = total;\n        }\n```\n\t\t\t\t\n#### **Solution**:\n\n```\nclass Solution {\n    private int mod = 1000000007;\n    public int dieSimulator(int n, int[] rollMax) {        \n        if(n == 1) {\n            return rollMax.length;\n        }\n        \n        // dp[i][j] number of valid combination with i number of rolls and dice end with j+1 value.\n        // dp[i][rollMax.length] total number of valid combinations with i number of rolls. \n        long[][] dp = new long[n+1][rollMax.length + 1]; \n        for(int i = 0; i < rollMax.length; ++i) {\n            dp[1][i] = 1;\n        }\n        \n        // when there is 0 rolls there is only one combination (empty)\n        dp[0][rollMax.length] = 1;   \n        // total number with 1 roll; \n        dp[1][rollMax.length] = rollMax.length; \n        \n        long total = 0;\n        long totalEndWithCurNum = 0;\n        for(int i = 2; i <= n; ++i) {\n            total = 0;\n            for(int j = 0; j < rollMax.length; ++j) {\n                totalEndWithCurNum = 0; \n                // based on max allowed occurence to calculate the possible combinations end with j + 1\n                for(int k = 1; k <= rollMax[j] && i >= k; ++k) {\n\t\t\t\t\t// dp[i-k][rollMax.length] - dp[i-k][j]  is number of combinations that i-k rolls have that does not end with j+1\n                    // the rest k rolls with have consistent value of j + 1\n                    // in this way, the new combination is still valid.\n\t\t            totalEndWithCurNum += (dp[i-k][rollMax.length] - dp[i-k][j] + this.mod) % this.mod;\n                    totalEndWithCurNum %= this.mod;\n                }\n                \n                total += totalEndWithCurNum; \n                total %= this.mod;\n                dp[i][j] = totalEndWithCurNum;\n            }\n            \n            dp[i][rollMax.length] = total;\n        }\n        \n        return (int)total;\n    }\n}\n\n```
[]