2021.2.8 - 2021.2.9 算法练习
作者:互联网
837. 新21点
根据题意,分析爱丽丝持有卡牌的分数不同时,分数不超过N的概率。因为当爱丽丝手上卡牌的分数大于等于K时,爱丽丝就不会再抽卡,因此爱丽丝手上卡牌的分数就在 [0,K + W - 1]这个区间。那么,对于爱丽丝不会再抽卡的区间,也就是 [K, K + W - 1],由于不会再抽卡,因此分数不会超过N的概率就是固定的 。如果N小于K,那么爱丽丝手中持有卡牌的分数在 [K, K + W - 1] 这个区间内,所求概率一定是0。同样的,如果N大于K + W - 1,那么所求概率一定是1。如果N在[K, K + W - 1]这个区间内,那么小于等于N时,概率为1,大于N时,概率为0。
那么,现在已经明确了爱丽丝不会抽卡时,持有分数不超过N的概率了。那么对于还需要抽卡时的情况就需要再进行讨论。例如对于持有分数为K - 1时,此时爱丽丝还会继续抽卡,而抽到的每一张卡是等概率的,也就是说,抽完卡后,爱丽丝持有分数就可能是K,K + 1,K + 2,K + 3 …… K + W -1,而概率都是相同的,因此当爱丽丝持有分数为K - 1时,最后分数不超过N的概率就是K,K + 1,K + 2,K + 3 …… K + W -1的平均概率。
所以,假设爱丽丝持有分数为i,最后分数不超过N的概率为d(i)时
\[dp(i) = 1/W * (dp(i + 1)+ dp(i + 2)....+dp(i + W)) \]那么这就是一个状态转移方程,所以这题可以使用动态规划来解决,由于可以明确的概率是在[K, K + W - 1],因此要推导出dp(0)时,就需要从后往前推动
代码(C++)
class Solution {
public:
double new21Game(int N, int K, int W) {
vector<double> dp(K + W, 0) ;
double t = 0 ;
for (int i = K + W - 1;i >= K;i--) {
if (i <= N) dp[i] = 1 ;
t += dp[i] ;
}
for (int i = K - 1;i >= 0;i--) {
dp [i] = t / W ;
t = t - dp[i + W] + dp[i] ;
}
return dp[0] ;
}
};
代码(Java)
class Solution {
public double new21Game(int N, int K, int W) {
double[] dp = new double[K + W] ;
double t = 0 ;
for (int i = K + W - 1;i >= K;i--) {
if(i <= N) dp[i] = 1.0 ;
t += dp[i] ;
}
for (int i = K - 1;i >= 0;i--) {
dp[i] = t / W ;
t = t - dp[i + W] + dp[i] ;
}
return dp[0] ;
}
}
1641. 统计字典序元音字符串的数目
这题可以类比成完全背包问题求解组合数吧,把N当成背包容量,aeiou当成物品,每个物品的重量都是1,同时要求有序,所以可以当成求解组合数来解决
class Solution {
public:
int countVowelStrings(int n) {
vector<int> dp(n + 1, 0) ;
dp[0] = 1 ;
for (int i = 1;i <= 5;i++) {
for (int j = 1;j <= n;j++) {
dp[j] += dp[j - 1] ;
}
}
return dp[n] ;
}
};
650. 只有两个键的键盘
官方题解中给出了素数分解的方法,这里使用的是动态规划
先想象一下如何获得5个'A'
- 当记事本上有3个'A'时,再复制2个'A'
- 当记事本上有4个'A'时,再复制1个'A'
- 当记事本上有2个'A'时,再复制3个'A'
- 当记事本上有1个'A'时,再复制4个'A'
那么这些情况合理吗?
-
当记事本上有4个'A'时,再复制1个'A'
合理,我们可以最开始就复制1个'A',一个一个复制就行
-
当记事本上有2个'A'时,再复制3个'A'
不合理,由于复制只能一次性复制全部,因此不可能在只有2个'A'的情况下,复制面板中有3个'A'
-
当记事本上有1个'A'时,再复制4个'A'
不合理,原因同上
-
当记事本上有3个'A'时,再复制2个'A'
很难一下子分析出来,因此需要再去分析如何获得3个'A'
所以,获得n个'A'的最少次数,取决于前面的状态,所以可以使用动态规划
假设dp(i, j)等于,当记事本上有i个'A',且复制面板中有j个'A'时所需要的最少操作次数
因此我们可以得出状态转移方程
\[dp(i, j) = dp(i-j,j) + 1\ \ \ \ i/2>j\ \ \ and \ \ \ dp(i-j,j)有效 \]\[dp(i,i) = min(dp(i,j)) + 1\ \ \ \ i/2 >j\ \ \ and\ \ \ dp(i-j,j)有效 \](j < i/2的原因是,i-j < j时,dp(i - j,j)是无效的)
class Solution {
public:
int dp[1010][1010] ;
int minSteps(int n) {
if (n == 1) return 0 ;
dp[1][1] = 1 ;
int minn = INT_MAX ;
for (int i = 2;i <= n;i++) {
minn = INT_MAX ;
for (int j = 1;j <= i / 2;j++) {
if (dp[i - j][j] == 0) continue ;
dp[i][j] = dp[i - j][j] + 1 ;
minn = min(minn, dp[i][j]) ;
}
dp[i][i] = minn + 1 ;
}
return minn ;
}
};
1105. 填充书架
本题可以这样想,如果要放下一本新的书,应该怎么放?
可以把这本书放到新的一层,也可以把这本书连同前几本书一起放到新的一层
为什么不到上一层?
因为放到上一层,就相当于把前面的所有书一起放到新的一层,这个情况已经包含在内了
对于一本书的放法所导致的最低高度,取决于前面几本书的摆放,所以本题可以使用动态规划
设dp(i)为放置前i本书的最低高度
class Solution {
public:
int dp[1010] ;
int minHeightShelves(vector<vector<int>>& books, int shelf_width) {
dp[0] = 0 ;
int h, w ;
int n = books.size() ;
for (int i = 1;i <= n;i++) {
h = 0;
w = 0 ;
dp[i] = INT_MAX ;
for (int j = i;j > 0;j--) {
h = max(h, books[j - 1][1]) ;
w += books[j - 1][0] ;
if (w > shelf_width) {
break ;
}
dp[i] = min(dp[i], dp[j - 1] + h) ;
}
}
return dp[n] ;
}
};
992. K 个不同整数的子数组
K个不同整数的子数组的个数可以由(最多K个不同整数的子数组数 - 最多K-1个不同整数的子数组数)获得,因此本题可以转换为求取最多N个不同整数的子数组数
那么求取最多N个不同整数的子数组数可以使用双指针解决:left指针指向子数组左端,right指针指向子数组右端+ 1。哈希表f记录当前子数组每个字符的出现此时,count表示子数组有多少个不同的整数。
子数组是[left, right)这个区间的元素
class Solution {
public:
int f[20010] ;
int subarraysWithKDistinct(vector<int>& A, int K) {
return helper(A, K) - helper(A, K - 1) ;
}
int helper(vector<int>& A, int K) {
int len = A.size() ;
memset(f, 0, sizeof(f)) ;
int left = 0 ;
int right = 0 ;
int count = 0 ;
int res = 0 ;
while (right < len) {
if (f[A[right]] == 0) {
count ++ ;
}
f[A[right]] ++ ;
right ++ ;
while(count > K) {
f[A[left]] -- ;
if (f[A[left]] == 0) {
count -- ;
}
left ++ ;
}
res += right - left ;
}
return res ;
}
};
牛客题霸 反转链表
用pre指代前一个链表节点,用cur指代当前节点,那么链表反转的操作就是:
- 存储cur的下一个节点为临时节点t
- 将cur下一个节点指向pre
- 让pre更新为cur
- cur更新为t
- 重复这个过程知道cur指向空指针,则链表循环结束
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if (pHead == nullptr) {
return nullptr ;
}
if (pHead->next == nullptr) return pHead ;
ListNode* pre = nullptr ;
ListNode* cur = pHead ;
ListNode* tmp = nullptr ;
while (cur != nullptr) {
tmp = cur->next ;
cur->next = pre ;
pre = cur ;
cur = tmp ;
}
return pre ;
}
};
标签:分数,2021.2,cur,int,练习,算法,复制,爱丽丝,dp 来源: https://www.cnblogs.com/Suans/p/14394110.html