C++引用型別作為函式返回值型別的簡單瞭解
A. Rainbow Dash, Fluttershy and Chess Coloring (CF 1393 A)
題目大意
給定一個\(n\times n\)的棋盤,要黑白間隔塗色。每次只能選擇一些格子塗色,這些格子必須與某些已經塗色的格子相鄰,問最小塗色次數。初始時最外圍的一圈都可以塗色。
解題思路
開始看樣例有種輸出n-1的衝動
想象一下塗色情況可知,第一次塗最外圍一圈的白色,第二次塗最外圍的黑色和第二外圍的黑色,第三次塗第二外圍的白色和第三外圍的白色……
若n是偶數,則最後是塗一個四個格子的兩個顏色,然後還要一次塗另一個顏色,次數是\(\dfrac{n}{2} + 1\)
若n是奇數,最後一次則是塗一個格子,次數是\(\lfloor \dfrac{n}{2} \rfloor + 1\)
綜上,答案就是\(\dfrac{n}{2} + 1\)(整除)。
神奇的程式碼
#include <bits/stdc++.h> using namespace std; typedef long long LL; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int kase; cin>>kase; for (int ii = 1; ii <= kase; ii++) { int n; cin>>n; cout<<(n/2+1)<<endl; } return 0; }
B. Applejack and Storages (CF 1393 B)
題目大意
初始有\(n\)塊木板,並已知每塊木板的長度。現有\(q\)次操作,每次操作會增加或者移除已有的長度為x的木板,問,每次操作後,是否能從這些木板中選出一些木板來,作出一個矩形和一個正方形。(正方形也是特殊的矩形)
解題思路
統計一下同一長度4個4個的有多少份,2個2個的有多少份,判斷就可以了。
神奇的程式碼
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } const int N = 1e5+8; int n,q,l; int cnt[N]; multiset<int> two,four; char s[5]; int main(void) { read(n); for(int u,i = 1; i <= n; ++ i){ read(u); if (cnt[u] % 4 == 3){ four.insert(u); } if (cnt[u] % 2 == 1){ two.insert(u); } cnt[u]++; } read(q); while(q--){ scanf("%s",s); read(l); if (s[0] == '+'){ if (cnt[l] % 4 == 3){ four.insert(l); } if (cnt[l] % 2 == 1){ two.insert(l); } cnt[l]++; }else{ if (cnt[l] % 4 == 0){ four.erase(four.find(l)); } if (cnt[l] % 2 == 0){ two.erase(two.find(l)); } cnt[l]--; } if (four.size() >= 2) puts("YES"); else if (four.size() == 0) puts("NO"); else if (two.size() >=4) puts("YES"); else puts("NO"); } return 0; }
C. Pinkie Pie Eats Patty-cakes (CF 1393 C)
題目大意
給定\(n\)個數字,要求重新排列這n個數字,使得同一個數字之間的距離的最小值最大。兩個相同數的距離即這兩個數之間的數字個數。
解題思路
很顯然出現次數最多的數字是決定答案的關鍵。設數字中出現最多的次數為ma次,我們將這ma個數字作為隔板,隔板之間能夠放的最多的數字即為\(\frac{n - ma}{ma - 1}\),至於餘數可以放到最後一個隔板的右邊,總存在一種方式,其他數字的最小距離不會大於隔板間的數量。
但如果出現次數為ma次的數字不止一個,而是有cnt個,我們就可以把這cnt個捆綁在一起,即一個長度為cnt的隔板。
所以最終答案即為\(\dfrac{n - ma * cnt}{ma - 1} + cnt - 1\)
神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
int main(void) {
int kase; read(kase);
for (int ii = 1; ii <= kase; ii++) {
int n;
read(n);
vector<int> tot(n+1);
int ma = 0;
int cnt = 0;
for(int a, i = 1; i <= n; ++ i){
read(a);
tot[a]++;
if (ma < tot[a]){
ma = tot[a];
cnt = 1;
}else if (ma == tot[a])
++cnt;
}
int ans = (n - ma * cnt) / (ma - 1) + cnt - 1;
write(ans,'\n');
}
return 0;
}
D. Rarity and New Dress (CF 1393 D)
題目大意
給定一張網格圖,問由相同字母組成的菱形個數有多少。一個格子也算是一個菱形。
解題思路
一個菱形有一個所謂的中心,從資料範圍來看我們自然不能從中心BFS來得到它能拓展多少。
我們把菱形拆成中心所在的一條線,上三角形和下三角形。
我們設\(up[i][j]\)表示以\((i,j)\)為中心的上三角形的長度(值為左或右拓展的距離),\(down[i][j]\)為下三角形,\(l[i][j]\)為從\((i,j)\)向左拓展的距離,\(r[i][j]\)為向右拓展的距離,則\((i,j)\)點所形成的最大菱形即為\(\min(up[i][j],down[i][j])\)
轉移
\(up[i][j] = \min(up[i-1][j], \min(l[i][j], r[i][j])\)
\(down[i][j] = \min(down[i+1][j], \min(l[i][j], r[i][j])\)
還有一個不太理解的,是設\(dp[i][j]\)表示以\((i,j)\)為底部點,最大拓展的長度。(1,1)是左上角。
而對於以\((i,j)\)為底部點的菱形,可以看做是以\((i-1,j)\)為底部點的菱形往下邊拓展一格,或者以\((i-1,j-1)\)為底部點的菱形往右邊拓展一格,或者以\((i-1,j+1)\)為底部點的菱形往左邊拓展一格。由於往右邊或往右邊拓展一格時,頂部的格子會往上拓展一個,此時又有\((i-2,j)\)的限制?
至於會取\(dp[i][j] = \min(dp[i-1][j],dp[i-2][j],dp[i-1][j-1],dp[i-1][j+1]) + 1\),如果\((i,j),(i-1,j),(i-1,j-1),(i-1,j+1),(i-2,j)\)字母相同的話,否則\(dp[i][j] = 1\)
神奇的程式碼
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
const int N = 2e3+8;
char s[N][N];
LL up[N][N],down[N][N],l[N][N],r[N][N];
LL ans;
int n,m;
int main(void) {
read(n);
read(m);
for(int i = 0; i < n; ++ i){
scanf("%s",s[i]);
}
for(int i = 0; i < n; ++ i){
for(int sign=0,cnt=1,j = 0; j < m; ++ j){
if (s[i][j] != sign){
sign = s[i][j];
cnt = 0;
}
++cnt;
l[i][j] = cnt;
up[i][j] = down[i][j] = 1;
}
for(int sign=0,cnt=1,j = m-1; j >= 0; -- j){
if (s[i][j] != sign){
sign = s[i][j];
cnt = 0;
}
++cnt;
r[i][j] = cnt;
}
}
for(int i = 1; i < n; ++ i){
for(int j = 0; j < m; ++ j){
if (s[i][j] == s[i-1][j]){
up[i][j] = min(up[i-1][j] + 1, min(l[i][j], r[i][j]));
}
}
}
for(int i = n-1; i >= 0; -- i){
for(int j = 0; j < m; ++ j){
if (s[i][j] == s[i+1][j]){
down[i][j] = min(down[i+1][j] + 1, min(l[i][j], r[i][j]));
}
}
}
for(int i = 0; i < n; ++ i)
for(int j = 0; j < m; ++ j)
ans += min(up[i][j],down[i][j]);
write(ans,'\n');
return 0;
}