【解題報告】Codeforces Round #303 (Div. 2)
A.Toy Cars(Codeforces 545A)
思路
簡單實現題。將表示碰撞結果的矩陣儲存下來,然後檢查每個車輛是否是“good car“即可。
程式碼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;
bool good;
int n, G[maxn][maxn];
vector <int> v;
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
scanf("%d", &G[i][j]);
}
good = true;
for(int j = 1; j <= n; j++) {
if(i != j && G[i][j] % 2) {
good = false;
}
}
if(good) v.push_back(i);
}
printf("%d\n", v.size());
for (int i = 0; i < v.size(); i++) {
printf("%d ", v[i]);
}
puts("");
return 0;
}
B.Equidistant String(Codeforces 545B)
思路
要構造一個與兩個輸入字串的漢明距離都相等的字串,只要從一個字串出發“走過”兩個字串漢明距離的一半就可以了。具體地,假設輸入的串為
程式碼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
char s[maxn], t[maxn];
int n, m, cnt;
vector <int> v;
int main() {
scanf("%s%s", s, t);
cnt = 0;
n = strlen(s);
for(int i = 0; i < n; i++) {
if(s[i] != t[i]) {
v.push_back(i);
cnt++;
}
}
if(cnt % 2) puts("impossible");
else {
m = cnt >> 1;
for(int i = 0; i < m; i++) {
s[v[i]] = t[v[i]];
}
printf("%s\n", s);
}
return 0;
}
C.Woodcutters(Codeforces 545C)
大意
在一條數軸上有若干棵樹。每棵樹有一個高度,問最多能砍多少棵樹,使得沒有倒下的樹碰到其它的樹。
思路
這題最直觀的解法是搜尋。搜尋樹的每一層都表示一次決策,即該層表示的樹是往左倒還是往右倒。但由於
- 第i棵樹直立的時候。不論第
i 棵想要直立是可以“不看前一棵樹的臉色”的,因此有d[i][0]=max{d[i−1][j],0≤j≤2} 。 - 第i棵樹向左倒的時候。第i棵樹想要向左倒,就必須“看前一棵樹的臉色”,也就是看前一棵樹給它留下了多少空位。當前一棵樹沒有向右倒的時候有
d[i][1]=max(d[i−1][0],d[i−1][1])+1 ,當前一棵樹向右倒的時候有d[i][1]=max(d[i][1],d[i−1][2]+1) 。在實現的時候要注意判斷位置是否足夠讓一棵樹倒下。 - 第i棵樹向右倒的時候。這種情況的最優值一定比第
i 棵樹直立的時候要多1 ,因此d[i][2]=d[i][0]+1 。
最後的答案就是
程式碼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10, INF = 1e9;
int n, ans, x[maxn], h[maxn], d[maxn][3];
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d%d", &x[i], &h[i]);
}
// 設定兩棵虛擬的樹
x[0] = - INF;
x[n+1] = INT_MAX;
for(int i = 1; i <= n; i++) {
// 直立
d[i][0] = max(max(d[i-1][0], d[i-1][1]), d[i-1][2]);
// 向左倒
if(h[i] < x[i] - x[i-1]) {
d[i][1] = max(d[i-1][0], d[i-1][1]) + 1;
}
if(h[i] + h[i-1] < x[i] - x[i-1]) {
d[i][1] = max(d[i][1], d[i-1][2] + 1);
}
// 向右倒
if(h[i] < x[i+1] - x[i]) {
d[i][2] = d[i][0] + 1;
}
}
printf("%d\n", d[n][2]);
return 0;
}
思路
其實本題也不是沒有貪心策略。對於兩端的樹而言,左端的樹往左倒,右端的樹往右倒一定是最優的策略。對於其它的樹(從左向右掃描的話),先考慮向左倒再考慮向右倒一定是最優策略。(證明方法暫時沒想到)
程式碼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int n, ans, x[maxn], h[maxn];
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d%d", &x[i], &h[i]);
}
ans = 2;
for(int i = 2; i < n; i++) {
if(x[i] - x[i-1] > h[i]) {
ans++;
}
else if(x[i+1] - x[i] > h[i]) {
x[i] += h[i];
ans++;
}
}
printf("%d\n", n <= 2 ? n : ans);
return 0;
}
D.Queue(Codeforces 545D)
大意
有若干個人在排隊等待服務,服務員服務每個人的時間都是不同的,對於一個人而言,若他前面的人的被服務總時間大於它自己被服務的時間,他就會感到失望。問最多可以讓多少人感到不失望。
思路
拿到這道題,直覺告訴我應該是先對資料排序(因為感性認知告訴我忍耐值小的人放在前面似乎能夠滿足儘可能多的人)然後再微調(感性認知不一定是事實)。為了方便討論我們用O表示排好序的佇列中不會失望的某個人,用X表示排好序的佇列中會失望的某個人。在微調的過程中,首先
所以我們總結出演算法:先對輸入資料排序,然後掃描排序後資料的同時維護字首和
程式碼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int n, cnt, sum, a[maxn];
int main() {
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
sort(a, a + n);
cnt = sum = 0;
for(int i = 0; i < n; i++) {
if(a[i] >= sum) {
sum += a[i];
cnt++;
}
}
printf("%d\n", cnt);
return 0;
}
E.Paths and Trees(Codefoeces 545E)
大意
在一個圖
思路
這個問題同時具有最小生成樹和最短路徑的特點,但最小生成樹無法算出起點到每個點的最短路徑,因此只考慮用後者來解決問題。
假設我們已經在原圖上跑了一遍