第207場周賽
第一次參加周賽,只ac出了一道題目,後面還是先加強正確率,再考慮速度。這裡記錄的程式碼來自比賽中排名第一,第二的大佬———zqy1018,Heltion
5519. 重新排列單詞間的空格(easy)
題目描述:給你一個字串 text ,該字串由若干被空格包圍的單片語成。每個單詞由一個或者多個小寫英文字母組成,並且兩個單詞之間至少存在一個空格。題目測試用例保證 text 至少包含一個單詞 。
請你重新排列空格,使每對相鄰單詞之間的空格數目都 相等 ,並儘可能 最大化 該數目。如果不能重新平均分配所有空格,請 將多餘的空格放置在字串末尾 ,這也意味著返回的字串應當與原 text 字串的長度相等。
返回 重新排列空格後的字串 。
分析:沒什麼技巧,暴力求解即可,關鍵是算出空格的個數,分割出單詞,這裡我採用stringstream處理,簡單方便易懂。
class Solution { public: string reorderSpaces(string text) { vector<string> ve; int blank_count = 0; blank_count = cal_blank(text); string buf; stringstream ss(text); while(ss>>buf){ ve.push_back(buf); } string str = rearranging_fun(ve, blank_count); if(str.size()==text.size()) cout<<"true"<<endl; return str; } int cal_blank(string text){ int l = text.length(); int n = 0; for(int i=0;i<text.length();i++){ if(isalpha(text[i])) n++; } return (l-n); } string rearranging_fun(vector<string> ve, int blank_count){ string re_str = ""; int str_count = ve.size(); if(ve.size()==1){ re_str += ve[0]; for(int i=0;i<blank_count;i++){ re_str += " "; } } else{ int num = blank_count / (str_count-1); int num_re = blank_count % (str_count-1); string blank = ""; for(int i=0;i<num;i++){ blank += " "; } string blank_re = ""; for(int i=0;i<num_re;i++){ blank_re += " "; } re_str += ve[0]; for(int i=1;i<str_count;i++){ re_str += blank; re_str += ve[i]; } re_str += blank_re; } return re_str; } };
問題:計算需要4ms,步驟太多繁雜。
5520. 拆分字串使唯一子字串的數目最大(medium)
題目描述:給你一個字串 s ,請你拆分該字串,並返回拆分後唯一子字串的最大數目。
字串 s 拆分後可以得到若干 非空子字串 ,這些子字串連線後應當能夠還原為原字串。但是拆分出來的每個子字串都必須是 唯一的 。
注意:子字串 是字串中的一個連續字元序列。
分析:DP是用不了的,每一次選擇與狀態都會與上一次有關係,這裡用回溯搜尋最好,加上兼職,速度應該是不是太慢的,這裡是子集樹.
class Solution { public: int ans; int maxUniqueSplit(string s) { ans=1; unordered_set<string> has; dfs(s,0,has); return ans; } void dfs(const string& s,int cur,unordered_set<string>& has){ if(s.size()-cur+has.size()<=ans) return; if(cur == s.size()){ ans=max(ans,(int)has.size()); } else{ for(int len=1;cur+len-1<s.size();++len){ string sub=s.substr(cur,len); if(!has.count(sub)){ has.insert(sub); dfs(s,cur+len,has); has.erase(sub); } } } } };
新增上的剪枝就是考慮剩餘的字元長度加上已經切分好的字元長度,也沒有最優值高,後面的就不需要搜尋了
5521. 矩陣的最大非負積(medium)
題目描述:給你一個大小為 rows x cols 的矩陣 grid 。最初,你位於左上角 (0, 0) ,每一步,你可以在矩陣中 向右 或 向下 移動。
在從左上角 (0, 0) 開始到右下角 (rows - 1, cols - 1) 結束的所有路徑中,找出具有 最大非負積 的路徑。路徑的積是沿路徑訪問的單元格中所有整數的乘積。
返回 最大非負積 對 10^9 + 7 取餘 的結果。如果最大積為負數,則返回 -1 。
注意,取餘是在得到最大積之後執行的。
分析:給定矩陣及其元素,DP求解,使用DP陣列,解決重疊子問題;這裡有一個注意的地方,就是這裡是乘積,元素也有正有負,最大值可以由上一狀態的最小值轉變而來,也可以由上衣狀態的最大值轉變而來,所以,在這裡要記錄每個狀態下的最大最小值。邊界條件是第一行和第一列。
class Solution {
long long mini[16][16], maxi[16][16];
public:
int maxProductPath(vector<vector<int>>& grid) {
int n = grid.size(), m = grid[0].size();
mini[0][0] = maxi[0][0] = grid[0][0];
for (int i = 1; i < m; ++i)
mini[0][i] = maxi[0][i] = mini[0][i - 1] * grid[0][i];
for (int i = 1; i < n; ++i){
mini[i][0] = maxi[i][0] = mini[i - 1][0] * grid[i][0];
for (int j = 1; j < m; ++j){
mini[i][j] = min(mini[i - 1][j] * grid[i][j], mini[i][j - 1] * grid[i][j]);
mini[i][j] = min(mini[i][j], maxi[i][j - 1] * grid[i][j]);
mini[i][j] = min(mini[i][j], maxi[i - 1][j] * grid[i][j]);
maxi[i][j] = max(maxi[i - 1][j] * grid[i][j], maxi[i][j - 1] * grid[i][j]);
maxi[i][j] = max(maxi[i][j], mini[i][j - 1] * grid[i][j]);
maxi[i][j] = max(maxi[i][j], mini[i - 1][j] * grid[i][j]);
}
}
if (maxi[n - 1][m - 1] < 0) return -1;
return maxi[n - 1][m - 1] % 1000000007ll;
}
};
程式碼簡潔易懂,用空間換時間,可能還不是最優解。
5522. 連通兩組點的最小成本
題目描述:給你兩組點,其中第一組中有 size1 個點,第二組中有 size2 個點,且 size1 >= size2 。
任意兩點間的連線成本 cost 由大小為 size1 x size2 矩陣給出,其中 cost[i][j] 是第一組中的點 i 和第二組中的點 j 的連線成本。如果兩個組中的每個點都與另一組中的一個或多個點連線,則稱這兩組點是連通的。換言之,第一組中的每個點必須至少與第二組中的一個點連線,且第二組中的每個點必須至少與第一組中的一個點連線。
返回連通兩組點所需的最小成本。
這題難度有點大,暫時還沒有想明白,後續想明白了再補上
constexpr int maxn = 12;
int sum[maxn][1 << maxn], Log[1 << maxn];
int dp[maxn][1 << maxn];
class Solution {
public:
int connectTwoGroups(vector<vector<int>>& cost) {
int n = cost.size();
int m = cost[0].size();
for(int i = 2; i < (1 << 12); i += 1) Log[i] = Log[i >> 1] + 1;
for(int x = 0; x < n; x += 1)
for(int i = 1; i < (1 << m); i += 1)
sum[x][i] = sum[x][i - (i & -i)] + cost[x][Log[i & -i]];
for(int i = 0; i < n; i += 1){
if(i == 0){
for(int j = 0; j < (1 << m); j += 1) dp[i][j] = sum[i][j];
dp[0][0] = 120000;
}
else{
for(int j = 0; j < (1 << m); j += 1){
dp[i][j] = 120000;
for(int k = j; k; k = (k - 1) & j)
dp[i][j] = min(sum[i][k] + dp[i - 1][j ^ k], dp[i][j]);
}
}
for(int j = 0; j < m; j += 1)
for(int k = (1 << m) - 1; k >= 0; k -= 1)
if(k & (1 << j)) dp[i][k ^ (1 << j)] = min(dp[i][k], dp[i][k ^ (1 << j)]);
if(0) for(int k = 0; k < (1 << m); k += 1){
cout << i << " " << k << " " << dp[i][k] << "\n";
}
}
return dp[n - 1][(1 << m) - 1];
}
};