阿里巴巴面试题: 卡牌游戏
描述
现在有一个卡牌游戏,先给出卡牌的数量n,再给你两个非负整数totalProfit、totalCost,然后给出每张卡牌的利润值 a[i]和成本值b[i],现在可以从这些卡牌中任意选择若干张牌,组成一个方案,问有多少个方案满足所有选择的卡牌利润和大于totalProfit且成本和小于totalCost。
- 由于这个数可能很大,因此只需返回方案数对1e9 + 7取模的结果。
- 0≤n≤100
- 0≤totalProfit≤100
- 0≤totalCost≤100
- 0≤a[i]≤100
- 0≤b[i]≤100
在线评测地址
样例1
输入:n = 2,totalProfit = 3,totalCost = 5,a = [2,3],b = [2,2]
输出:1
解释:
此时只有一个合法的方案,就是将两个卡牌都选上,此时a[1]+a[2] = 5 > totalProfit 且 b[1] + b[2] < totalCost,满足题意。
样例 2:
输入:n = 3,totalProfit = 5,totalCost = 10,a = [6,7,8],b = [2,3,5]
输出: 6
解释:
假设一个合法方案(i,j)表示选择了第i张卡牌和第j张卡牌。
则此时合法的方案有:
(1),(2),(3),(1,2),(1,3),(2,3)
解题思路
假设dp[i][j]dp[i][j]为卡牌利润和等于ii且成本和等于jj的方案数。 则按顺序枚举每一个卡牌xx,同时更新dpdp数组,有: dp[i+a[x]][j+b[x]]+=dp[i][j]dp[i+a[x]][j+b[x]]+=dp[i][j]
dp递推关系式的推导可以类比背包问题,代码中的totalProfit+1即表示利润大于totalProfit的方案数,totalCost同理,因为有: int now_p = min(totalProfit + 1, p + ai); int now_c = min(totalCost + 1, c + bi); 限制Profit最大为totalProfit + 1,Cost最大为totalCost + 1。
时间复杂度:O(n∗totalProfit∗totalCost+totalCost)
源代码
public class Solution
{
/**
* @param n: The number of cards
* @param totalProfit: The totalProfit
* @param totalCost: The totalCost
* @param a: The profit of cards
* @param b: The cost of cards
* @return: Return the number of legal plan
*/
public int numOfPlan(int n, int totalProfit, int totalCost, int[] a, int[] b) {
// Write your code here
long mod = 1000000007;
long [][][]dp;
dp = new long [150][150][150];
int i, j, k;
for (i = 0; i <= 105; i++)
for (j = 0; j <= 105; j++)
for (k = 0; k <= 105; k++) {
dp[i][j][k] = 0;
}
dp[0][0][0] = 1;
for (k = 0; k < n; k++)
for (i = 0; i <= totalProfit + 1; i++)
for (j = 0; j < totalCost; j++)
if (dp[k][i][j] > 0) {
dp[k + 1][i][j] += dp[k][i][j];
dp[k + 1][i][j] %= mod;
if (j + b[k] < totalCost) {
dp[k + 1][Math.min(totalProfit + 1, i + a[k])][j + b[k]] += dp[k][i][j];
dp[k + 1][Math.min(totalProfit + 1, i + a[k])][j + b[k]] %= mod;
}
}
long sum;
sum = 0;
for (j = 0; j < totalCost; j++) {
sum = (sum + dp[n][totalProfit + 1][j]) % mod;
}
return (int)sum;
}
}