D - Celester Editorial
by
physics0523
TL;DR: 動的計画法 (DP) でこの問題を解くことができます。
この問題は、以下のような構造を持っています。
- \(i=1,2,\dots,N\) について、以下を繰り返す。
- \(i\) 日目の天気を決める。
- \(i-1\) 日目の天気と \(i\) 日目の天気によって、嬉しさを得られることがある。
例えば、各日の晴雨を \(2^N\) 通り全探索する方法が考えられます。(これら全てを調べ尽くす必要はありますが、全探索してしまっては実行時間制限に間に合いません。)
しかし、例えば \(10\) 日目を見ている時に \(1,2,\dots,9\) 日目について 晴雨雨晴…晴 のような情報を持つのは過剰そうです。
ここで、「 \(i\) 日目に嬉しさを得られるかどうかを判定するには \(i-1\) 日目だけの天気を覚えておけばよい」という性質を利用します。
\(i-2\) 日目以前の情報をうまく捨てることで、必要な情報だけが残った状態にした上で似た状態をまとめて効率的に扱います。
ここで、以下の DP テーブルを導入します。
- \(dp[\)
S\(] \) = { 前日の天気が 晴れ である場合の、前日までの嬉しさの最大値 }
- \(dp[\)
R\(] \) = { 前日の天気が 雨 である場合の、前日までの嬉しさの最大値 }
確かに、一昨日かそれ以前の天気の情報は捨てられており、前日の天気だけを覚えておくということができそうです。
この \(dp\) を計算することができれば本問題が解けそうです。
まず、 \(dp\) を何から始めればいいかを検討します。これを DPの初期化 と呼びます。
- \(1\) 日目の天気を決定する。
- もし \(S\) の \(1\) 文字目が
Sなら、 \(dp[\)S\(]=0,dp[\)R\(]=-X_1\) です。- この場合、 \(1\) 日目の天気を雨に変更する場合のみ、嬉しさに \(X_1\) の損失が出ます。
- もし \(S\) の \(1\) 文字目が
Rなら、 \(dp[\)S\(]=-X_1,dp[\)R\(]=0\) です。- この場合、 \(1\) 日目の天気を晴れに変更する場合のみ、嬉しさに \(X_1\) の損失が出ます。
ここから始めて、 \(2\) 日目以降に関して実際に計算しましょう。現在の \(dp\) から次の \(dp\) (以降、 \(ndp\) と書くことにします) を求める行為を DPの遷移 と呼びます。
\(ndp\) は次の通りに計算できます。
- \(i\) 日目の天気を決定する。
- \(ndp[\)
S\(]=\max(dp[\)S\(],dp[\)R\(]+Y_{i-1})\) とする。- \(i-1\) 日目の天気が雨、 \(i\) 日目の天気が晴れというケースにおいて、 \(Y_i\) の嬉しさを獲得します。\(i-1\) 日目の天気が晴れ、 \(i\) 日目の天気が晴れでも特に嬉しさを獲得することはありません。
- このタイミングで、これ以降は不要となる \(i-1\) 日目の情報を捨てて、代わりに \(i\) 日目の情報を持ちます。
- \(ndp[\)
R\(]=\max(dp[\)S\(],dp[\)R\(])\) とする。- \(i\) 日目の天気が雨であるとき、特別に嬉しさを獲得することはありません。
- ただし、このままだと \(i\) 日目の天気の変更にかかる対価を反映していません。以下のようにして反映させます。
- \(S\) の \(i\) 文字目が
Sであるとき、 \(ndp[\)R\(]\) から \(A_i\) 減算する。 - \(S\) の \(i\) 文字目が
Rであるとき、 \(ndp[\)S\(]\) から \(A_i\) 減算する。
- \(S\) の \(i\) 文字目が
全ての日について天気を決定した後、 \(dp[\) S \(]\) には \(N\) 日目が晴れである場合の最適値が、 \(dp[\) R \(]\) には \(N\) 日目が雨である場合の最適値が入っています。なので、最終的な答えは \(\max(dp[\) S \(],dp[\) R \(])\) とすればよいです。
各日について「晴れ→晴れ」「晴れ→雨」「雨→晴れ」「雨→雨」の \(4\) 通りの遷移だけを調べればよいので、この解法の時間計算量は \(O(N)\) です。
実装例 (C++):
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int main(){
int t;
cin >> t;
while(t--){
int n;
cin >> n;
string str;
cin >> str;
vector<ll> x(n),y(n-1);
for(auto &nx : x){cin >> nx;}
for(auto &nx : y){cin >> nx;}
ll s=0,r=0;
if(str[0]=='R'){s=-x[0];}
else{r=-x[0];}
for(int i=1;i<n;i++){
ll ns=max(s,r+y[i-1]);
ll nr=max(s,r);
if(str[i]=='R'){ns-=x[i];}
else{nr-=x[i];}
s=ns;
r=nr;
}
cout << max(s,r) << "\n";
}
return 0;
}
posted:
last update:
