我能贏嗎
輸入:
maxChoosableInteger = 10
desiredTotal = 11
輸出:
false
思路:我們首先來看如果給定的數(shù)字范圍大于等于目標(biāo)值的話,直接返回true。如果給定的數(shù)字總和小于目標(biāo)值的話试溯,說明誰也沒法贏爪瓜,返回false传于。然后我們進(jìn)入遞歸函數(shù)盗忱,首先我們查找當(dāng)前情況是否在哈希表中存在格郁,有的話直接返回即可展箱。我們使用一個(gè)整型數(shù)按位來記錄數(shù)組中的某個(gè)數(shù)字是否使用過旨枯,我們遍歷所有數(shù)字,將該數(shù)字對(duì)應(yīng)的mask算出來混驰,如果其和used相與為0的話攀隔,說明該數(shù)字沒有使用過皂贩,我們看如果此時(shí)的目標(biāo)值小于等于當(dāng)前數(shù)字,說明已經(jīng)贏了昆汹,或者我們調(diào)用遞歸函數(shù)明刷,如果返回false,說明也是第一個(gè)人贏了满粗。為啥呢辈末,因?yàn)楫?dāng)前我們已經(jīng)選過數(shù)字了,此時(shí)就該對(duì)第二個(gè)人調(diào)用遞歸函數(shù)映皆,只有他的結(jié)果是false挤聘,我們才能贏,所以此時(shí)我們標(biāo)記true捅彻,返回true组去。如果遍歷完所有數(shù)字,我們標(biāo)記false沟饥,返回false
class Solution {
public:
bool canIWin(int maxChoosableInteger, int desiredTotal) {
if (maxChoosableInteger >= desiredTotal) return true;
if (maxChoosableInteger * (maxChoosableInteger + 1) / 2 < desiredTotal) return false;
unordered_map<int, bool> m;
return canWin(maxChoosableInteger, desiredTotal, 0, m);
}
bool canWin(int length, int total, int used, unordered_map<int, bool>& m) {
if (m.count(used)) return m[used];
for (int i = 0; i < length; ++i) {
int cur = (1 << i); //表示將1左移i位添怔,1,2,4,8...
if ((cur & used) == 0) {
if (total <= i + 1 || !canWin(length, total - (i + 1), cur | used, m)) {
m[used] = true;
return true;
}
}
}
m[used] = false;
return false;
}
};
猜數(shù)字大小(https://leetcode-cn.com/problems/guess-number-higher-or-lower-ii/)
從1-n中選一個(gè)數(shù),n = 10, 我選擇了8.
第一輪: 你猜我選擇的數(shù)字是5贤旷,我會(huì)告訴你广料,我的數(shù)字更大一些,然后你需要支付5塊幼驶。
第二輪: 你猜是7艾杏,我告訴你,我的數(shù)字更大一些盅藻,你支付7塊购桑。
第三輪: 你猜是9,我告訴你氏淑,我的數(shù)字更小一些勃蜘,你支付9塊。
游戲結(jié)束假残。8 就是我選的數(shù)字缭贡。
你最終要支付 5 + 7 + 9 = 21 塊錢。
給定 n ≥ 1辉懒,計(jì)算你至少需要擁有多少現(xiàn)金才能確保你能贏得這個(gè)游戲阳惹。
思路
dp[i][j]表示從[i,j]中猜出正確數(shù)字所需要的最少花費(fèi)金額.(dp[i][i] = 0)
假設(shè)在范圍[i,j]中選擇x, 則選擇x的最少花費(fèi)金額為: max(dp[i][x-1], dp[x+1][j]) + x
用max的原因是我們要計(jì)算最壞反饋情況下的最少花費(fèi)金額(選了x之后, 正確數(shù)字落在花費(fèi)更高的那側(cè))
初始化為(n+2)*(n+2)數(shù)組的原因: 處理邊界情況更加容易, 例如對(duì)于求解dp[1][n]時(shí)x如果等于1, 需要考慮dp[0][1](0不可能出現(xiàn), dp[0][n]為0)
而當(dāng)x等于n時(shí), 需要考慮dp[n+1][n+1](n+1也不可能出現(xiàn), dp[n+1][n+1]為0)
如何寫出相應(yīng)的代碼更新dp矩陣, 遞推式dp[i][j] = max(max(dp[i][x-1], dp[x+1][j]) + x), x~[i:j], 可以畫出矩陣圖協(xié)助理解, 可以發(fā)現(xiàn)
dp[i][x-1]始終在dp[i][j]的左部, dp[x+1][j]始終在dp[i][j]的下部, 所以更新dp矩陣時(shí)i的次序應(yīng)當(dāng)遵循bottom到top的規(guī)則, j則相反, 由于
i肯定小于等于j, 所以我們只需要遍歷更新矩陣的一半即可(下半矩陣)
public int getMoneyAmount(int n) {
int[][] dp = new int[n+2][n+2];
for(int i = n; i >= 1; --i) {
for(int j = i; j <= n; ++j) {
if(i == j)
dp[i][j] = 0;
else {
dp[i][j] = Integer.MAX_VALUE;
for(int x = i; x <= j; ++x)
dp[i][j] = Math.min(dp[i][j], Math.max(dp[i][x-1], dp[x+1][j]) + x); //為什么這個(gè)地方最外面是Math.min???
}
}
}
return dp[1][n];
}
nim游戲
你和你的朋友,兩個(gè)人一起玩 Nim 游戲:桌子上有一堆石頭眶俩,每次你們輪流拿掉 1 - 3 塊石頭莹汤。 拿掉最后一塊石頭的人就是獲勝者。你作為先手颠印。
你們是聰明人纲岭,每一步都是最優(yōu)解抹竹。 編寫一個(gè)函數(shù),來判斷你是否可以在給定石頭數(shù)量的情況下贏得游戲荒勇。
示例:
輸入: 4
輸出: false
解釋: 如果堆中有 4 塊石頭柒莉,那么你永遠(yuǎn)不會(huì)贏得比賽;
因?yàn)闊o論你拿走 1 塊沽翔、2 塊 還是 3 塊石頭兢孝,最后一塊石頭總是會(huì)被你的朋友拿走。
bool canWinNim(int n) {
return n%4==0?false:true;
}
變態(tài)跳臺(tái)階
一只青蛙一次可以跳上1級(jí)臺(tái)階仅偎,也可以跳上2級(jí)……它也可以跳上n級(jí)跨蟹。求該青蛙跳上一個(gè)n級(jí)的臺(tái)階總共有多少種跳法。
f(1)=1, f(2)=2, f(3)=4, f(4)=8 設(shè)n+1級(jí)f(n+1),有
f(n+1) = f(1) + f(2) + ... + f(n)
f(n+2) = f(1) + f(2) + ... + f(n+1)
f(n+2)= 2f(1) + 2f(2) + ... + 2f(n)
故得f(n+2) = 2f(n+1)
矩形覆蓋
我們可以用 2 * 1 的小矩形橫著或者豎著去覆蓋更大的矩形橘沥。請(qǐng)問用n個(gè)2 * 1的小矩形無重疊地覆蓋一個(gè)2*n的大矩形窗轩,總共有多少種方法?
f(1) = 1
f(2) = 2
f(n) = f(n-1) + f(n-2)
孩子們的游戲
有n個(gè)數(shù)座咆,從0開始編號(hào)痢艺,每次刪除第m個(gè)數(shù),下一輪循環(huán)從m+1開始介陶。求最后剩下的數(shù)字是多少
雖然有遞推公式堤舒,但是用鏈表模擬更好
public int LastRemaining_Solution(int n, int m) {
LinkedList<Integer> list = new LinkedList<>();
for(int i=0;i<n;i++)
list.add(i);
int a = 0;
while(list.size() > 1)
{
a = (a+m-1) % list.size();
list.remove(a);//每次刪除,直到留下最后的那一個(gè)哺呜!
}
if(list.size() == 1)
return list.get(0);//數(shù)組只剩下最后的一個(gè)
return -1;
}
n舌缤!末尾有多少個(gè)0
思路:5!=54321某残,0的個(gè)數(shù)和5的個(gè)數(shù)相等0