D. Timetable
Problem - D - Codeforces
轉化成揹包問題
dp預處理
很容易看出來是揹包,但是怎麼把區段內的時間轉化成物品很吃力。
首先物品的體積肯定不能直接用題目給的時間點,而是用逃課的數量。
然後對物品的價值進行預處理
- 預處理出每天的日期
- 每次處理一天留下 \(k\) 個 \(1\) 的物品之前就先跑一遍找出貢獻最小的方案
- 然後用這個方案來跑揹包就行。
資料很小,不會超時
constexpr int N(500 + 10);
std::vector<int> sched[N];
int dp[N][N];
void solve() {
int n, m, k;
std::cin >> n >> m >> k;
std::string s;
for (int i = 1; i <= n; i++) {
std::cin >> s;
for (int j = 0; j < m; j++) {
if (s[j] == '1') {
sched[i].push_back(j);//預處理出每一天的1的日期
}
}
}
memset(dp, 0x3f, sizeof dp);
dp[0][0] = 0;
for (int i = 1; i <= n; ++i) {
int siz(sz(sched[i]));
for (int cnt = 0; cnt <= siz; ++cnt) {
int res = m;//先求出當前選擇cnt個1的能得到的最小貢獻
if (cnt) {
for (int l = 0, r = cnt - 1; r < siz; ++l, ++r) {//暴力列舉一邊所有區間
res = std::min(res, sched[i][r] - sched[i][l] + 1);
}
}
else {//如果是選擇0個,那顯然貢獻就是0
res = 0;
}
int cntSkip(siz - cnt);
for (int l = 0; l + cntSkip <= k; ++l) {//已經轉化成了揹包問題
dp[i][l + siz - cnt] = std::min(dp[i][l + cntSkip], dp[i - 1][l] + res);
}
}
}
std::cout << *std::min_element(dp[n], dp[n] + k + 1) << '\n';
}
E. Largest Beautiful Number
Problem - E - Codeforces
貪心構造和bitmask的妙用
void solve()
{
#define tests
std::string s;
std::cin >> s;
int n(sz(s));
std::string res;
for (int i = 0; i < n - 2; i++) {
res += '9';
}
for (int lcp = n - 1; lcp >= 0; lcp--) {//從右到左檢查,一旦遇到合適的位就輸出
if (s[lcp] != '0') {
for (char c = s[lcp] - 1; c >= (lcp == 0 ? '1' : '0'); c--) {//列舉該位的所有可能數碼
std::string cur = s.substr(0, lcp) + c;
int mask = 0;//用位來當數碼,標記這一位對應的數碼是否出現偶數次
for (int i = 0; i < sz(cur); i++) {
mask ^= (1 << (cur[i] - '0'));
}
std::string me;
for (int i = 9; i >= 0; i--) {
if ((mask >> i) & 1) {
me += (i + '0');//為落單的湊,從大到小湊,儘量接近給的數
}
}
while (sz(cur) + sz(me) < sz(s)) {//剩下的全部湊9
cur += '9';
}
cur += me;
if (sz(cur) == sz(s)) {
res = cur;
break;
}
}
if (sz(res) == sz(s)) {
break;
}
}
}
std::cout << res << '\n';
}
F. Fibonacci String Subsequences
Problem - F - Codeforces
\(\texttt{kmp}\) 原理
矩陣
考慮每個 \(F(x)\) 中與 \(s\) 相同的子序列的貢獻。設這個子序列為 \(F(x)_{p_1}, F(x)_{p_2}, F(x)_{p_3}, \ldots, F(x)_{p_n}\)。
我們想要它成為一個子序列的子串,那麼 \(F(x)_{[p_1, p_n]}\) 中除了 \(p_1 \sim p_n\) 就不能有別的字元被選。而 \([1, p_1 - 1] \cup [p_n + 1, |F(x)|]\) 中的每個字元都可以選或不選,相當於每個字元產生 \(2\) 的貢獻。
如果我們設 \(f_{i, j}\) 為 kmp 過程中考慮到 \(F(x)\) 的第 \(i\) 位,匹配到 \(s\) 的第 \(j\) 位的答案,那麼相當於
\(f_{i + 1, 0} \gets 2 f_{i, 0}, f_{i + 1, n} \gets 2 f_{i, n}, \forall j \in [1, n - 1], f_{i + 1, j} \gets f_{i, j}, \forall j \in [1, n] \land F(x)_i = s_j, f_{i + 1, j} \gets f_{i, j - 1}\)。
答案即為 \(f_{|F(x)|, n}\)。
可以用矩陣刻畫這個轉移過程。設 \(F_i\) 為 \(F(i)\) 的轉移矩陣,那麼 \(F_i = F_{i - 1} F_{i - 2}\)。
時間複雜度 \(O(n^3x)\)。
struct mat {
Z a[N][N];
mat() {
memset(a, 0, sizeof a);
}
}dp[N];
mat operator * (const mat &a, const mat &b) {
mat res;
for (int k = 0; k <= n; ++k) {
for (int i = 0; i <= n; ++i) {
for (int j = 0; j <= n; ++j) {
res.a[i][j] += a.a[i][k] * b.a[k][j];
}
}
}
return res;
}
void solve() {
std::cin >> n >> m >> s;
dp[0].a[0][0] = dp[0].a[n][n] = dp[1].a[0][0] = dp[1].a[n][n] = 2;
for (int i = 1; i < n; i++) {
dp[0].a[i][i] = dp[1].a[i][i] = 1;
}
for (int i = 1; i <= n; i++) {
if (s[i - 1] == '0') {
dp[0].a[i - 1][i] = 1;
}
else {
dp[1].a[i - 1][i] = 1;
}
}
for (int i = 2; i <= m; i++) {
dp[i] = dp[i - 1] * dp[i - 2];
}
mat ans;
ans.a[0][0] = 1;
ans = ans * dp[m];
std::cout << ans.a[0][n] << '\n';
}
G. Almost Increasing Array
Problem - G - Codeforces
CF946G - Almost Increasing Array - 洛谷專欄 (luogu.com.cn)
過於困難,先放著