來源:【C++ 取模mod易錯點】由於答案可能會很大,請你將結果對1e9+7取模后再返回_白馬金羈俠少年的博客-CSDN博客
在做算法題時我們經常會遇到這樣一句話:
由於答案可能會很大,請你將結果對10^9 + 7取模后再返回
- 1000000007是一個質數
- int32位的最大值為2147483647,所以對於int32位來說1000000007足夠大
- int64位的最大值為2^63-1,對於1000000007來說它的平方不會在int64中溢出 所以在大數相乘的時候,因為(a∗b)%c=((a%c)∗(b%c))%c,所以相乘時兩邊都對1000000007取模,再保存在int64里面不會溢出
這句話看上去只要對變量取模就可以了,但實際上取模的時機有一定的講究,比如新手很容易犯一下兩個錯誤
錯誤1:用max比較很大數據時,先取模
取mod的時候,如果題目要求你算最大值,並且說由於答案可能很大,輸出結果請對1e9+7取,那你千萬不能在max函數更新最大值時就取模,這樣很可能會出錯
比如:題目過程中有四個數據
2e9+7,1e9+6,1e9+5,1e9+4
然后算法中你用max求最大值時,如果先模上1e9+7,那你會得到1e9,1e9+6,1e9+5,1e9+4
,並且max函數算出的最大值是1e9+6,可是這四個數的最大值應該是2e9 + 7
才對
正確做法:在求max的時候不要先取mod,而是都以long long型數據比大小,最后得到最大值是2e9 + 7
,再對它取mod,得到結果是1e9 + 7
錯誤代碼示例
class Solution {
public:
// 乘積 = 某個節點下所有子節點的和 *(整個樹的和 - 某個節點下所有子節點的和)
typedef long long LL;
LL rmax = 0;
LL Total = 0;
static const LL mod = 1e9 + 7;
// 遍歷每個結點,更新最大值
LL TreeSum(TreeNode* root) {
if (!root) return 0;
LL left = TreeSum(root->left);
LL right = TreeSum(root->right);
LL subsum = left + right + root->val;
// 此處先取模,所以數據很大時會出錯
rmax = max(rmax, (Total - subsum) * subsum % mod);
return subsum;
}
int maxProduct(TreeNode* root) {
if (!root) return 0;
Total = TreeSum(root);
rmax = 0;
TreeSum(root);
return rmax % mod;
}
};
錯誤2:直到return時才取模
如果讓你算1+2+…+n的值(由於答案可能很大,輸出結果請對1e9+7取)
n的取值范圍是1 ~ 1 0 10000 10^{10000} 1010000
那顯然如果你在中間過程中不先取mod,必然會爆數據范圍,因為不管是int還是long long甚至是double(最大約 1 0 308 10^{308} 10308)都無法存下這個數據
...
long long res = 0;
for(int i = 1; i <= n ; i ++) {
res = res + i; // n巨大,res無法存下這個數據
}
return res % (1e9 + 7);
正確做法:在算res的過程中取模
...
long long res = 0;
for(int i = 1; i <= n ; i ++) {
res = (res + i) % (1e9 + 7); // n巨大,res無法存下這個數據
}
return res;
有的人會說這不是與錯誤1矛盾了嗎,其實完全不矛盾
- 錯誤1是在比較
多個
很大的數據(不超過long long) - 錯誤2是在維護
1個
很大的數據,那你先取模和后取模,就沒有區別了,因為結果都是一樣的
另外有的時候我們經常喜歡用 +=
之類的符號,但容易犯以下錯誤
long long res = 0;
for(int i = 1; i <= n ; i ++) {
res += i % (1e9 + 7); // n巨大,res無法存下這個數據
}
return res;
上面的代碼實際上並沒有對res進行取模,而只是對 i i i進行取模,當res累加很多數后,是會超過long long,從而出現報錯的。所以你要么在下面加一行res %= (1e9 + 7)
,要么用res = (res + i) % (1e9 + 7);
現在看面試題:剪繩子II這個題,就知道為什么不能用動態規划了,因為取余之后max函數就不能用來比大小
數據較小時下面代碼是可以過的,數據大了就不行了,因為不能在max出取模
class Solution {
public:
// f(n) = max(f(i) * f(n - i)) i:1,..,n-1
int cutRope(int number) {
// number = 1,2,3時剪一刀,值會變小,所以特殊考慮這幾種情況
if(number < 2) return 0;
if (number == 2) return 1;
if (number == 3) return 2;
int *f = new int[number + 1];
f[1] = 1;
f[2] = 2;
f[3] = 3;
// f(n) n大於3時,表示切若干刀后的最大值
for (int i = 4; i <= number; i ++) {
int rmax = number;
for (int j = 1; j <= i / 2; j ++) {
rmax = max(rmax, f[j] * f[i - j]);
}
f[i] = rmax;
}
int res = f[number];
delete[] f;
return res;
}
};
錯誤3:是否真的取模了?
下面的式子取模過程有個坑點:
a[i][j]並沒有取模就直接與 pre[i][j]相乘了(*
和%
是從左往右算的)
res = res % mod + pre[i][j] % mod * a[i][j] % mod;
正確的寫法:
res = res % mod + pre[i][j] % mod * (a[i][j] % mod);