FZU Monthly-201901 tutorial
題目(難度遞增) | easy | easy-medium | medium | medium-hard | hard |
---|---|---|---|---|---|
思維難度 | AHG | F | B | CE | D |
編碼難度 | AH | CEFG | B | D |
A. RonTanYoYiSen
對於本題,你只需要FOR一遍判斷是不是回文串即可。不管一個字符串是不是回文串,只有將它反過來再寫一邊就可以構造出一個回文串。根據題意,如果是回文串則輸出“YES NO”,否則輸出“NO YES”。
復雜度\(O(N)\)
B. max position set
Tutorial 1. Bruce force
由於是字典序的緣故,所以我們從第一列開始看到最后一列,如果當前列選擇后,不會違背字典序,那么根據貪心思想這一列要,否則顯然不能取。這樣做 \(N\) 次,每次把新的一列加到前面選出來的字符串后面,check一下是否合法。總復雜度 \(O(N^3)\)。
例子:
adc
bcd
加進第一列
a
b
合法,保留。加入第二列
ad
bc
不合法,撤銷。加入第三列
ac
bd
合法,保留。
Tutorial 2. Observe
觀察可以發現,每加入一列后,如果第 \(i\) 行的字典序小於第 \(i+1\) 行,那么之后怎么怎么加都不會再對着這兩行有影響,即:整個過程中需要考慮的一定是相鄰的且所有字符均一樣的字符串。所以標記一下 \(N-1\) 個相鄰關系中國哪些已經出現偏序,那些還沒有,不需要考慮前面的取的字符,只需要比較當前新加進的這一列即可(因為前面一定是一樣的)
這樣還是做 \(N\) 次,每次比較只花 \(O(N)\) 的時間,總復雜度 \(O(N^2)\)。
C.Palindrome
畫圖把相同的字符連線會發現就只有4種情況,然后分類討論一下
-
m1:當m1時,任意長度為1的子串顯然都是回文串,所以答案是 \(k^n\)
-
m > n:與m==1情況是一樣的
-
m==n:這個時候只需要考慮前半部分,這時候答案分別是 \(k^{n/2}\)(n為偶數),\(k^{n/2+1}\)(n為奇數)
-
m < n: 當m為奇數,答案為\(k^2\),當m為偶數,答案為\(k\)
注:m > n 的時候,顯然怎么構造都滿足題意
復雜度為\(O(N)\)
D. special square
同樣的題目背景,經典題型是求面積最大的全1矩陣。先學習一下這道題的做法,關於這道題,演算法筆記上有非常詳盡的分析:http://www.csie.ntnu.edu.tw/~u91029/MaximumSubarray.html#2
這題的做法基本與那道經典題類似,一樣的按行or列枚舉,通過單調棧去check,核心就是滿足條件的矩形四條邊都要頂到障礙物,代碼基本與原經典題類似,理解原經典題后如還有細節有疑惑可見代碼。
復雜度為\(O(N^2)\)
//
// solution.cpp
// special-matrix
//
// Created by 鄭浩暉 on 2018/12/19.
// Copyright © 2018 鄭浩暉. All rights reserved.
//
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define sz(x) ((int)(x).size())
#define dd(x) cout << #x << " = " << (x) << ", "
#define de(x) cout << #x << " = " << (x) << endl
typedef pair<int, int> pii;
const int N = 5e3 + 7;
char s[N][N];
int n, m, up[N][N];
void solve()
{
cin >> n >> m; m++;
for (int i = 0; i < n; i++) { scanf("%s", s[i]); s[i][m-1] = '0'; }
for (int i = 0; i < m; i++) s[n][i]='0';
for (int j = 0; j < m; j++) up[0][j] = s[0][j] == '1';
for (int i = 1; i < n; i++) {
for (int j = 0; j < m; j++) {
up[i][j] = s[i][j] == '1' ? up[i - 1][j] + 1 : 0;
}
}
int ans = 0;
for (int i = 0; i < n; i++) {
stack<pii> stk;
int max_col = -1;
for (int j = 0; j < m; j++) {
int pos = j;
while (!stk.empty() && stk.top().second > up[i][j]) {
if (stk.top().first <= max_col)
ans++;
pos = stk.top().first;
stk.pop();
}
if (s[i + 1][j] == '0') max_col = j;
if (!stk.empty() && stk.top().second == up[i][j]) continue;
if (up[i][j]) stk.push({pos, up[i][j]});
}
}
cout << ans << endl;
}
int main()
{
solve();
return 0;
}
E.LiHuaAndPoker
分類討論
- K = 1。這個情況,字符串的相對位置不變,所以可能最多只有n種(每一個字母做開頭),對n種情況做一個比較即可,復雜度可以是O(n),O(nlogn),O(n^2),O(n^2logn)均可以通過。
- K > 1。對於K大於1的所有情況,任何字符串都能調整成他的最小字典序(想想為什么),即對原字符串從小到大排序。對於K>2的排序情況類似冒泡排序。
F.LiHuaAndArray
一個經典的標記技巧(延遲計算)。對所有的[L,R]區間,在L位置標記+1,在R位置標記-1。標記結束后統計每一對奇偶位置的前綴和,前綴和是奇數的就交換位置,是偶數的不做操作。
復雜度O(N)。
G.Assassin
模擬題,分階段模擬
- 負一從A點出發追小號
- 負一追到小號后回A點
注:有個學弟 “小號已經走的路程” 用了int變量,太心疼了
H.DeadlyShoot
這個題目在大一的c語言日常題目中出現過。
Tutorial 1. Bruce force
對着題意模擬一直做數位求和直到一位,然后分類找出最大的一組輸出。
Tutorial 2. Math
我們設一個數A的數位和為S,那么易得A和S同余於9。由此,反復求數位和直到只剩一位這個操作等價於對9取模。因此,按照所有數對9的余數進行分類,最后輸出余數最大的那一組即可,需要特別注意的是,當某個數能被9整除的時候,他最后得到的數位和是9而不是0。
復雜度為\(O(N)\)
**
**
博主只是友情提供代發服務