CSAcademy Subarray Partition
<問題>
長さの配列が与えられる。
を、同じ数は全て1つの連続する部分列に含まれるように分けるとき、最大でいくつに分割できるか。
(たとえば5がの 5,9, 12番目に含まれるとすると少なくとも5~12番目を1つの部分列に含める必要がある。)
<解説>
事前にmap[ ]での現れる最大のindexを指すようにmapを構築します。
を調べながら、それまで出てきた最大のmap[ ]を持ち、
になれば(現在までの数が全て含まれている)答えを1インクリメントしていきます。
<コード>
int main() { int n; cin >> n; vi a(n); rep(i, n)cin >> a[i]; map<int, int> m; rep(i, n) { m[a[i]] = i; } int ans = 0; int max_i = 0; rep(i, n) { max_i = max(max_i,m[a[i]]); if (i == max_i) { ans++; } } P(ans); return 0; }
<感想>
問題文と公式解説(こっちは理解していない)が難しかった。
CSAcademy Equal Sums
<問題>
の行列が与えられる。
に1加えるという操作を考えるとき
のすべての行の和・列の和を等しくするとき、
操作は最低何回必要か。
<解説>
元の[tex : A]の行の和・列の和の最大値をとするとき、
すべての行の和・列の和をにすればよいです。(シミュレーション)
(時間が遅いので証明はTODOとさせてください。。。)
特に難しいことはないですが、
に足す値は行目の和と列目の和を考える必要がありますが、
毎回計算していてはTLEしてしまうので、事前に行・列の和を計算しておき、
の更新とともに、更新しましょう。
<コード>
int a[1005][1005]; int main() { int n; cin >> n; rep(i, n)rep(j, n)cin >> a[i][j]; int maxa = 0; rep(i, n) { int tmp = 0; rep(j, n)tmp += a[i][j]; maxa = max(maxa, tmp); } rep(i, n) { int tmp = 0; rep(j, n)tmp += a[j][i]; maxa = max(maxa, tmp); } int sumr[1005], sumc[1005]; MS(sumr, 0); MS(sumc, 0); rep(i, n)rep(j, n) { sumr[i] += a[i][j]; sumc[j] += a[i][j]; } rep(i, n)rep(j, n) { int diff = max({ 0,min(maxa-sumr[i],maxa-sumc[j]) }); a[i][j] += diff; sumr[i] += diff; sumc[j] += diff; } rep(i, n) { rep(j, n) { if (j == n - 1) { printf("%d\n",a[i][j]); } else { printf("%d ",a[i][j]); } } } return 0; }
<感想>
1000^3ギリギリ通りそうだと思ったけど、普通にTLEでした。
CSAcademy Prefix Matches
<問題>
長さの文字列、サイズの配列,が与えられます。
はの番目の文字から何文字がのprefixと一致するかを表します。
はの番目で終わる部分文字列のうち何文字がのprefixと一致するかを表します。
が与えられたときを答えよ。
<解説>
の更新方法には2種類あります。
1.
文字目から文字がのprefixと一致するので、となります。
(Aから○文字がprefixと一致するなら逆からみても一致します。)
2.
(から文字目がのprefixと一致しているならから文字目も一致します)
<コード>
int main() { int n; cin >> n; vi a(n), b(n); for (int i = 1; i < n; i++) { cin >> a[i]; } for (int i = 1; i < n; i++) { b[i + a[i] - 1] = max(b[i + a[i] - 1], a[i]); } for (int i = n - 2; i > 0; i--) { b[i] = max(b[i],b[i+1]-1); } for (int i = 1; i < n; i++) { if (i != n - 1) { cout << b[i] << " "; } else { P(b[i]); } } return 0; }
<感想>
問題文を理解するのに時間がかかった
CSAcademy Max Substring
<問題概要>
文字列が与えられます。の連続する部分列のうち、その文字列の出現回数が最大のものを見つける。
ただし、そのような部分列が複数ある場合には、長さが最大のものを、
それでも複数ある場合には辞書順最小のものを答えなさい。
<解説>
a~zの26のアルファベットの出現回数のうち、最も多いものを回
とすると、答える部分文字列はすべて回現れるアルファベットで構成されます。
回以外の出現回数のアルファベットが含まれていると、部分文字列として出現回数が最大になりません。
また、すべてのアルファベットの出現回数が1の場合は単純にが答えになります。
答えるべき文字列を探索する方法ですが、に含まれる文字列はすべての出現場所でを構成している必要があります。
の最初のアルファベットを全探索し、各アルファベットごとに、文字列の2文字目のアルファベットの出現箇所が1文字目の出現箇所に比べて1増加していれば、
2文字の候補を作れます。(すべての出現箇所で連続になっているかどうかチェックします)
同様に3文字目、4文字目と作っていき、それぞれの最大文字数のうち、辞書順最小のものが答えになります。
コードではでアルファベットの番目の出現場所として、チェックしています。
また、には同じ文字は出現しないので、になります。
<コード>
int main() { string s; cin >> s; int n = s.length(); vi d[26]; rep(i, n) { d[s[i] - 'a'].pb(i); } int max_count = 0; rep(i, 26) { max_count = max( max_count, (int)d[i].size() ); } if (max_count == 1) { P(s); return 0; } string ans=""; int max_length = -1; rep(i, 26) { if (d[i].size() != max_count) continue; int tmp_length = 0; while (true) { bool ok = true; rep(j, max_count - 1) { if (d[i][j + 1] + tmp_length >= n || s[d[i][j] + tmp_length] != s[d[i][j + 1] + tmp_length]) { ok = false; break; } } if (!ok)break; if (max_length < tmp_length) { max_length = tmp_length; ans = s.substr(d[i][0], tmp_length + 1); } else if (max_length == tmp_length) { ans = min(ans, s.substr(d[i][0], tmp_length + 1)); } tmp_length++; } } P(ans); return 0; }
<感想>
すべての文字列の出現回数が1の場合でハマっていた。。。
解説の日本語が冗長ですみません。
chmod遅すぎ問題
chmodが遅すぎる問題が発生したので、chmodに関して色々実験
(virtual boxからクリップボード共有ができなかったので、virtual boxで書いてます。すでに重い・・・)
<環境>
アーキテクチャ: x86_64 CPU 操作モード: 32-bit, 64-bit バイト順序: Little Endian CPU: 2 オンラインになっている CPU のリスト: 0,1 コアあたりのスレッド数: 1 ソケットあたりのコア数: 2 ソケット数: 1 NUMA ノード数: 1 ベンダー ID: GenuineIntel CPU ファミリー: 6 モデル: 69 モデル名: Intel(R) Core(TM) i7-4510U CPU @ 2.00GHz ステッピング: 1 CPU MHz: 2593.998 BogoMIPS: 5187.99 ハイパーバイザのベンダー: KVM 仮想化タイプ: 完全仮想化 L1d キャッシュ: 32K L1i キャッシュ: 32K L2 キャッシュ: 256K L3 キャッシュ: 4096K NUMA ノード 0 CPU: 0,1
シェルスクリプトの名前が変わらないのは許してください。
まずは普通にfor文書いてみます。
kou6839@kou6839-VirtualBox:~$ cat for.sh for i in `seq 10000` do : done
・計測
kou6839@kou6839-VirtualBox:~$ time ./for.sh real 0m0.050s user 0m0.031s sys 0m0.016s
普通に早い
・次にtouch 10000回
kou6839@kou6839-VirtualBox:~$ cat for.sh for i in `seq 10000` do touch a done
計測
kou6839@kou6839-VirtualBox:~$ time ./for.sh real 0m10.846s user 0m6.315s sys 0m2.582s
まぁまぁ遅い
次にchmod 10000回
kou6839@kou6839-VirtualBox:~$ cat for.sh touch a for i in `seq 10000` do chmod 777 a done
計測
kou6839@kou6839-VirtualBox:~$ time ./for.sh real 0m10.470s user 0m6.161s sys 0m2.473s
touchとほぼ同じくらい
次にchmodでモードを変えてるのと変えてないのでは時間がかわるのか実験
ループ回数を半分にして中の処理を2つにしています
kou6839@kou6839-VirtualBox:~$ cat for.sh touch a for i in `seq 5000` do chmod 777 a chmod 444 a done
計測
kou6839@kou6839-VirtualBox:~$ time ./for.sh real 0m10.332s user 0m7.440s sys 0m3.075s
別に変えても変えてなくても時間は同じっぽい
・次にchmodの引数を複数にするとはやいらしいので、その準備
ファイルa, bのモードを変えます
kou6839@kou6839-VirtualBox:~$ cat for.sh touch a touch b for i in `seq 5000` do chmod 777 a chmod 777 b done
計測
kou6839@kou6839-VirtualBox:~$ time ./for.sh real 0m10.408s user 0m7.395s sys 0m3.209s
1個1万回と2個5000回ずつなのであまりかわりません。
引数を2つにします
kou6839@kou6839-VirtualBox:~$ cat for.sh touch a touch b for i in `seq 5000` do chmod 777 a b done
計測
kou6839@kou6839-VirtualBox:~$ time ./for.sh real 0m5.311s user 0m3.700s sys 0m1.595s
はやい!
最初の2つ(touchや1ファイルのchmod)と比較すると2つのファイルのchmodだと引数に関わりなく、timeコマンドの結果からIO待ちが少ないことがわかります。
さらに、引数を増やすと並列なのかforkが少なくなるのかわからんが、とにかく早い!
5000回chmodすることはないかと思いますが、再帰的に(-R)chmodするときには、使えるかもしれません。
そもそもchmod以外でも使える技なので、使ってみてください!
CSAcademy Lonely Points
<問題概要>
要素数の数列が与えられる。
数列Aは昇順にならんでおり、点のx座標を表している。
一度だけ、数列Aの数字1つを選び、好きな座標(数字)に書き換えることができる。
このような操作をし、隣り合った(N-1つ)の座標の差の最大値を最小にしたとき、その値はいくつか。
<解説>
一番差が大きいところに、どこからか数字を持ってきて2つの数字の真ん中に置き、差を半分にします。それと、2番目に大きいものを比較し、大きいほうが答えになります。 (コードではpriority_queueで管理しています)
一番差が大きいもの以外の間においても答えは変わらないので、その必要はありません。
次に、どこから数字を持ってくるのかということを考えますが、両端のどちらか隣との差が大きいほうになります。
端の数字を持ってくる場合は、その隣との座標の差そのものがなくなるため、差の最大値が増えることがありません。
一方、端ではない数字を移動させる際にはが増加してしまい、移動する際のペナルティが発生します。
後者でも答えに影響がないこともありますが、ペナルティがない方を選んだほうが実装も楽です。
<コード>
int main(){ int n; cin >> n; vi a(n); rep(i, n)cin >> a[i]; //[tex:{A_0}を移動させる場合] priority_queue<int> pq; for (int i = 1; i + 1 < n; i++) { pq.push(a[i + 1] - a[i]); } int maxp = pq.top(); pq.pop(); pq.push((maxp + 1) / 2); int ans = pq.top(); //[tex:{A_{N-1}}を移動させる場合] pq = priority_queue<int>(); for (int i = 0; i + 1 < n-1; i++) { pq.push(a[i + 1] - a[i]); } maxp = pq.top(); pq.pop(); pq.push((maxp + 1) / 2); ans = min(ans, pq.top()); P(ans); return 0; }
LPIC レベル2取得
今日の午後にLPIC 202試験に合格したので、LPIC level2取得になりました。
LPIC japanのマイページの履歴によると
2018/2/19 101 score 750
2018/3/4 102 score 740
2018/4/30 201 score 600
と取ってきており、今回
2018/5/13 202 score 680
ということになります。
そもそも、なぜ取ろうと思ったかというと、業務知識獲得のためです。
(正確な名称がわかりませんが、)ナビを動作させるマイコンをlinuxでのtftpブートやnfsマウントなどで起動させる必要があるが、linuxの経験が、scp,コンパイル,ELF実行くらい(研究)しかなかったため、さっぱりわかりませんでした。
毎回毎回ググってやるのも面倒だなと思い、体系立てて勉強できるLPICの受験を決心しました。
ただ、勉強を始めるとオプション暗記ゲーで面倒だな・・・と思ったことも多かったです。
レベル2に関しては、スコアも低く、実際にサーバーを立てたのもDNS,apache,メールサーバーの3つしかありません。。。
ですので、レベル2の一部、実務とは逸れていますが、基本的には勉強してよかった資格だったと思っています。
たとえば、systemdやディレクトリ知識などはとても役立っています。
(ただし、ウチの会社だと試験費・昇給一切出ないので、レベル1レベル2受験料と教材費合わせて8万近く自腹で払ってます!!(-.-))
最後に、他のサイトでお世話になったので、勉強法をすこし。
すべての試験で、ping-tとスピマスの2つで勉強しました。
割合的には7:3くらいで、問題数が多い分ping-tに時間を割いたと思います。
ping-tに関しては、すぐに答えを見ながら一度すべて金にし、そこからもう2週くりかえしました。(金の問題をALLで解いて銅におちたやつをすべて金に戻すと一周)
1週するのに、5時間はかかったような。
コマ問はなんか、ずれているような気がしたので全くやっていません。
スピマスについては大体3週くらいしました。
こちらは模擬試験はやっていません。
単純に、タブレット(電子書籍)でやると、問題と答えの一門一答がやりづらかったからです。
試験勉強を通してlinuxの知識はやるだけついたのですが、基本的なネットワークの知識が足りないことに気づきました。
ということで、今後の目標は、一度CCNA routing & swithingを取ってからレベル3を取ろうと思います。
頑張って6月中にレベル3まで目指します。