Codeforces Round 954 (Div. 3)
題目列表:
- A. X Axis
- B. Matrix Stabilization
- C. Update Queries
- D. Mathematical Problem
- E. Beautiful Array
- F. Non-academic Problem
- G1. Permutation Problem (Simple Version)
- G2. Permutation Problem (Hard Version)
A. X Axis
題目大意:
t組測試樣例,每組樣例給出三個整數,分別表示數軸上三個點的座標,選取數軸上任意一個點,使得這個點到給定的三個點的距離和最小,求出這個最小距離和。
分析:
純水題,顯然答案就是給定的這三個點中最大的座標減去最小的座標。
B.Matrix Stabilization題目大意:
C. Update Queries題目大意:
給你兩個字串S、C,長度分別為n和m,以及一個大小為m的陣列$ind[]$表示索引,有m次操作,從1到m依次進行,第i次操作需要把 $ S_{ind[i]} $變為$C_i$。並且你可以把字串$C$以任意順序排列,比如對於$C=abc$,你可以將它變為$acb,bca,bac,cab,cba$。求出m次操作之後所能得到的字典序下最小的$S$。 D.Mathematical Problem分析:
\(n\le20\) 這是 \(O(n^2)\) ?NO!
非 DP 整體複雜度 \(O(n)\) 可做!可以當成並不困難的規律題做。
思路分析:
首先我們要想到一些特殊情況(原因在程式碼註釋中解釋):
- \(n=2\) 時直接輸出原數;
- \(n=3\) 時,若第一位或第三位數為 0,則答案為 0;
- \(n>3\) 時,有 0 則答案為 0。
此題有一個顯而易見的性質:對於已知的一組數,除了 0 和 1 之外的數直接加和答案最小。
證明:顯然。其他題解中對於這一條已有很好的解釋,這裡不再多做敘述了,也可以自己手膜一下容易發現。
在想到這條性質後,那麼我們需要做的就是分出一個二位數使答案最優,以下我們稱要找的這個二位數為最優二位數。
按照容易想到的 \(O(n^2)\) 做法,就是遍歷出所有二位數,對每一個二位數,都求一遍 1 以為的數的加和,答案即為所有和的最小值。
那麼我們能不能直接 \(O(n)\) 找出最優二位數呢,再仔細分析一下,又發現以下性質:
(若兩個二位數 \(a,b\),有 \(a\) 優於 \(b\),則我們寫成 \(a>b\))於是有如下“優排列”(注意 11,21 等個位數為 1 的數的位置)
$ 12=13=14=15=16=17=18=19>11=22=...=29>21>...$
以此類推。(不含個位數為 0 的, 因為特殊情況中 0 已經考慮)即十位數相同的所有二位數,個位數不為 1 的數要優於個位數為 1 的。
為什麼呢?我們假定將原數列除 1 之外的總和為 \(sum\),找出最優二位數之後除 1 之外的總和為 \(you\),最優二位數為 12 時,則 \(you\) 相比於 \(sum\) 增加了 \(12-2=10\),(\(sum\) 中只加了 2 ,而 \(you\) 中加了 12,對於所有十位數為 1 的二位數,\(you-sum\) 的值都為 10,而對於 11 ,\(you-sum=11-0=11\) (因為 1 在 \(sum\) 都不進行計算)。\(11>10\),所以十位數相同時,個位數不為 1 的要優於個位數為 1 的。那麼十位數為 2、個位數不為 1 時,\(you-sum=18\),對於 21,\(you-sum=20\),同理,便得出了上述“優排列”。
這樣我們就可以 \(O(n)\) 找出最優二位數了,之後直接計算即可。
處理的時候我們可以分別找出最小的各位為 1 的二位數和各位不為 1 的二位數,比較它們十位數即可。
程式碼如下:
#include<bits/stdc++.h>
using namespace std;
const int N = 25;
int t, n, a[N];
int main(){
// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
scanf("%d", &t);
while(t--)
{
scanf("%d", &n); bool if_end = false;
for(int i=1; i<=n; i++){
scanf("%1d", &a[i]);
}
if(n == 2){ //n-2=0,即不需要新增運算子,直接輸出
int ans = 0;
ans = a[1] * 10 + a[2];
printf("%d\n", ans);
continue;
}
if(n == 3){
if(a[1] == 0 or a[3] == 0){//特判0
puts("0");
continue;
}
if(a[2] == 0){//n=3,只能有一個運算子,第二位數為0答案不為0
if(a[1] == 1) printf("%d\n", a[3]);
else if(a[3] == 1) printf("%d\n", a[1]);
else printf("%d\n", a[1]+a[3]);
continue;
}
}
int sm = 2008, id = 0;//記個位數不為1的最小二位數及位置
int _1 = 2008, id_1 = 0;//個位數為1的最小二位數及位置
for(int i=1; i<=n; i++){
if(n > 3 and a[i] == 0){ //有0,全部相乘便為0
puts("0"); if_end = true; break;
}
int now = a[i-1] * 10 + a[i];
if(i > 1 and a[i] == 1){
if(_1 > now) _1 = now, id_1 = i;
}
if(i > 1 and a[i] != 1){
if(sm > now) id = i, sm = now;
}
}
if(if_end) continue;
int y = sm - sm % 10;//y為個位數不為1的最小二位數的十位數
if(_1 < y and _1) sm = _1, id = id_1;//此時最優二位數為最小的個位數為1的二位數
int ans = sm;
for(int i=1; i<=n; i++){ //1之外的加和
if(i == id or i == id - 1 or a[i] == 1) continue;
ans += a[i];
}
printf("%d\n", ans);
}
return 0;
}