Leetcode Weekly Contest 38
這次的題目多了一點點要思考的成份,第四題忘了歸零有點可惜。不過總算是可以拿 T-shirt 了,開心~
給你 $n$ 個數字 $(3\le n\le 1000)$,問從中挑選三個數字,最大的乘積為何?數字範圍 $[-1000, 1000]$。
把數字都排好序以後,不難發現答案只會跟最小 & 最大的三個數有關。 因此我們可以把四種情形逐一嘗試:
class Solution {
public:
int maximumProduct(vector<int>& nums) {
sort(nums.begin(), nums.end());
int n = nums.size();
int ans = nums[0] * nums[1] * nums[2];
ans = max(ans, nums[0] * nums[1] * nums[n-1]);
ans = max(ans, nums[0] * nums[n-2] * nums[n-1]);
ans = max(ans, nums[n-3] * nums[n-2] * nums[n-1]);
return ans;
}
};
有 $n$ 堂課,每一堂課長度為 $t$,若收看的話必須在時間 $d$ 之前看完。請問至多可以看完幾堂課?$(1\le n, t, d\le 10000)$
首先,我們可以證明:假設 $C_1, C_2, \ldots, C_k$ 是一組可以被完成的課程,那麼依照他們的 deadline 時間排序依序進行課程必定是個合法的解。
(這題題解好難寫喔,尤其是證明。以後再說吧 :P )
這題的一個解法是先把所有課程依照結束時間排序,接下來依序檢查:看看能不能加入當前的課程集合,如果可以,就直接把它加進去。如果不行,就比較看看這堂課花的時間比「目前課程集合當中花最久的課」哪一個比較短,留比較短的課程下來。
下面的寫法用的是 multiset
,但用 priority_queue
比較恰當。
class Solution {
public:
int scheduleCourse(vector<vector<int>>& courses) {
for(auto &&x: courses) {
swap(x[0], x[1]);
}
sort(courses.begin(), courses.end());
multiset<int> all;
int ans=0, now=0;
for(auto &&x: courses) {
if(now + x[1] <= x[0]) {
now += x[1];
all.insert(x[1]);
ans=max(ans, (int)all.size());
} else if (!all.empty() && *all.rbegin() > x[1] ) {
now -= *all.rbegin();
all.erase(all.find(*all.rbegin()));
all.insert(x[1]);
now += x[1];
}
}
return ans;
}
};
給定 $n, k$ $(0\le n, k\le 1000)$,請問有多少個 $1$ 到 $n$ 的排列其逆序數對的數量恰好有 $k$ 個?
我們可以依序考慮插入的第 $i=1, 2, \ldots, n$ 個數字,每一個數字插入當前的排列都有可能造成逆序數對 $+0, +1, \ldots, +(i-1)$。因此我們可以把它寫成一個動態規劃的樣子:令 $\DP(i, j)$ 表示 $1$ 到 $i$ 的排列其逆敘述對數量恰好是 $j$ 的排列數。
我們可以列出他的遞迴式:
然後我們就會發現這個總和是一個連續和,因此若我們預處理 $S(i, j) = \DP(i, 0) + \DP(i, 1) + \cdots + \DP(i, j)$ 那麼前面便可以化簡成 $\DP(i, j) = S(i, j) - S(i, j-i)$。
int dp[1005][1005];
int S[1005][1005];
class Solution {
public:
int kInversePairs(int n, int k) {
const int MOD = 1e9+7;
// initialize
dp[0][0] = 1;
for (int j = 0; j <= k; j++) S[0][j] = 1;
// run dynamic programming
for (int i = 1; i <= n; i++)
for (int j = 0; j <= k; j++) {
dp[i][j] = (S[i-1][j] - (j<i? 0: S[i-1][j-i])) % MOD;
S[i][j] = ((j? S[i][j-1]: 0) + dp[i][j]) % MOD;
}
if (dp[n][k] < 0) dp[n][k] += MOD;
return dp[n][k];
}
};
在一個 $26\times 26$ 的 Excel 表格中,進行以下一些操作:
set(row, col, value)
: 將 (row
,col
) 這格的資料設定為value
get(row, col)
: 取得 (row
,col
) 這格的數值sum(row, col, ranges)
: 將 (row
,col
) 這格的值設為後面一連串range
的總和 要注意的是sum
操作是間接的,只要被 ranges 包含的任何一個格子資料有修改,這一格就會接著變動。 請寫一隻程式模擬這些操作。
硬做吧,每次 query 重算,關鍵是重疊的範圍至多算一次。 感覺上這題用 python 寫起來會方便很多很多。
class Excel {
public:
int a[27][27];
vector<string> refs[27][27];
int mode[27][27];
int dirt[27][27];
int H, W;
int dirt_cnt;
Excel(int H, char W) {
memset(a, 0, sizeof(a));
memset(mode, 0, sizeof(mode));
memset(dirt, 0, sizeof(dirt));
this->H = H;
this->W = (int)(W-'A')+1;
this->dirt_cnt = 0;
}
void set(int r, char c, int v) {
a[r][c-'A'+1] = v;
mode[r][c-'A'+1] = 0;
++dirt_cnt;
}
int get(int r, char c) {
++dirt_cnt;
return calc(r, c-'A'+1);
}
int sum(int r, char c, vector<string> strs) {
++dirt_cnt;
refs[r][c-'A'+1] = strs;
mode[r][c-'A'+1] = 1;
return calc(r, c-'A'+1);
}
private:
int calc(int x, int y) {
if (dirt_cnt == dirt[x][y]) return a[x][y];
dirt[x][y] = dirt_cnt;
if (mode[x][y] == 0) return a[x][y];
a[x][y] = 0;
for(auto &&xt: refs[x][y]) {
int p = xt.find(":");
if (p != string::npos) {
string u = "";
for(int i=0;i<p;i++) u += xt[i];
string v = xt.substr(p+1);
int r1, c1, r2, c2;
c1 = u[0]-'A'+1;
r1 = atoi(u.substr(1).c_str());
c2 = v[0]-'A'+1;
r2 = atoi(v.substr(1).c_str());
for(int i=r1;i<=r2;i++)
for(int j=c1;j<=c2;j++)
a[x][y] += calc(i, j);
//printf("x=%d, y=%d, cur=%d (%d,%d)->(%d,%d)\n", x, y, a[x][y], r1, c1, r2, c2);
} else {
string u = xt;
int r1, c1;
c1 = u[0]-'A'+1;
r1 = atoi(u.substr(1).c_str());
a[x][y] += calc(r1, c1);
}
}
return a[x][y];
}
};