EDPC解説 A~E

EDPC解説目次へ戻る

アライさんなのだ!
EDPCのA問題~E問題を解説するのだ!

A問題 Frog 1

1Nの番号がついた足場がある。
足場iからは足場i+1i+2に移動でき、足場iから足場jに移動するコストは|h[i]-h[j]|である。
足場1から足場Nに移動するまでの最小コストを求めよ。
制約:N\leq 10^5

解説

足場Nにたどり着くには足場N-1から来るか足場N-2から来るしかないのだ!
だから答えは
「足場N-1にたどり着くまでの最小コスト+|h[N-1]-h[N]|」と
「足場N-2にたどり着くまでの最小コスト+|h[N-2]-h[N]|」の小さい方になるのだ!
ということは
dp[i]=(足場iにたどり着くまでの最小コスト)
がわかってれば良さそうなのだ!

rep(i,2,N+1){
	if(i-2>=1)dp[i]=min(dp[i-1]+abs(h[i-1]-h[i]),dp[i-2]+abs(h[i-2]-h[i]));
	else dp[i]=dp[i-1]+abs(h[i-1]-h[i]);//i-2<1のときi-2の足場はないのだ!
}
ans=dp[N];

N回ループだから計算量O(N)なのだ。

B問題 Frog 2

1Nの番号がついた足場がある。
足場iからは足場i+1,i+2,\ldots,i+Kのいずれかに移動でき、足場iから足場jに移動するコストは|h[i]-h[j]|である。
足場1から足場Nに移動するまでの最小コストを求めよ。
制約:N\leq 10^5K\leq 10^2

解説

A問題と全く同じで答えは
「足場N-1にたどり着くまでの最小コスト+|h[N-1]-h[N]|
「足場N-2にたどり着くまでの最小コスト+|h[N-2]-h[N]|
……
「足場N-Kにたどり着くまでの最小コスト+|h[N-K]-h[N]|」の最小のものなのだ!
だからやっぱり
dp[i]=(足場iにたどり着くまでの最小コスト)
がわかれば良さそうなのだ!

rep(i,2,N+1){
	dp[i]=dp[i-1]+abs(h[i-1]-h[i]);
	rep(j,2,K+1)if(i-j>=1)dp[i]=min(dp[i],dp[i-j]+abs(h[i-j]-h[i]));
}
ans=dp[N];

外側のループがN回、内側のループがK回だから計算量はO(NK)なのだ。

C問題 Vacation

N日間のそれぞれの日についてA,B,Cの3つの行動のうち1つを選んで行う。
i日目にAをすると幸福度a[i]得る。同様にB,Cをするとb[i],c[i]を得る。
同じ行動を2日以上連続して選ぶことが出来ないとき、幸福度の和の最大値を求めよ。
制約:N\leq 10^5

解説

さっきまでの問題と同じように
dp[i]=(i日目までに得られる幸福度の和の最大値)
としたいけど、それじゃダメなのだ。例えばa[N]=1,b[N]=1,c[N]=1000だったときには、最終日にはCを選んだ方が良さそうだけど、前の日にCを選んでたら困るのだ!
「2日連続で選べない」という条件は、前の日に何をしたかわかってればどうにかできるから
dp[i][j]=(i日目に行動jをして、i日目までに得られる幸福度の和の最大値)
を考えれば良さそうなのだ!
i日目に行動Aをするには、前日の行動はBCじゃないとダメだから
dp[i][A]=\max(dp[i-1][B]+a[i],dp[i-1][C]+a[i])
になるのだ!dp[i][B]dp[i][C]も同様なのだ。
最終的な答えは、最終日の行動がどれでもいいから
\max(dp[N][A],dp[N][B],dp[N][C])
なのだ!
実際には文字を添え字に持つのは大変だから、A→0、B→1、C→2としてるのだ。

rep(i,1,N+1){
	dp[i][0]=max(dp[i-1][1]+a[i],dp[i-1][2]+a[i]);
	dp[i][1]=max(dp[i-1][0]+b[i],dp[i-1][2]+b[i]);
	dp[i][2]=max(dp[i-1][0]+c[i],dp[i-1][1]+c[i]);
}
ans=max(dp[N][0],max(dp[N][1],dp[N][2]));

A[i][0]=a[i],\ A[i][1]=b[i],\ A[i][2]=c[i]としてa[i],b[i],c[i]を1つの2次元配列にまとめて持つとループで書けて簡単なのだ!

rep(i,1,N+1)rep(j,0,3){
	rep(k,0,3)if(j!=k)dp[i][j]=max(dp[i][j],dp[i-1][k]+A[i][j]);
}
ans=max(dp[N][0],max(dp[N][1],dp[N][2]));

N回ループだから計算量O(N)なのだ。

D問題 Knapsack 1

N個の品物があり、品物iは重さw[i]、価値v[i]である。
重さの総和がW以下になるように品物を選ぶとき、選んだ品物の価値の総和の最大値を求めよ。
制約:N\leq10^2W\leq10^5v[i]\leq 10^9

解説

ナップサックDPなのだ!
dp[i]=(i番目までの品物を、重さがW以下になるように選んだときの価値の最大値)
じゃやっぱりダメで、次の品物を使っていいかどうかがわからないのだ。
次の品物を使っていいかどうかは、前までの重さがわかってればわかりそうな気がするから
dp[i][j]=(i番目までの品物を、重さがj以下になるように選んだときの価値の最大値)
を考えるのだ。するとdp[i][j]は、
i番目の品物を使うとき「i-1番目までで重さがj-w[i]以下の価値の最大値+v[i]
使わないとき「i-1番目までの重さがj以下の価値の最大値」
という2つの場合の大きい方になるのだ

rep(i,1,N+1)rep(j,0,W+1){
	if(j-w[i]>=0)dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
	else dp[i][j]=dp[i-1][j];//j-w[i]<0のときはi番目の品物は使えないのだ!
}
ans=dp[N][W];

外側のループがN回、内側のループがW回だから計算量はO(NW)なのだ。

E問題 Knapsack 2

N個の品物があり、品物iは重さw[i]、価値v[i]である。
重さの総和がW以下になるように品物を選ぶとき、選んだ品物の価値の総和の最大値を求めよ。
制約:N\leq10^2W\leq10^9v[i]\leq 10^3

解説

D問題と全く同じなのだ。…………なのだ?
W\leq10^9になってるから、全く同じじゃダメなのだ。
D問題とは添字と値を逆にして
dp[i][j]=(i番目までの品物を、価値がjになるように選んだときの最小重さ)
とすると解けるのだ。
最後にdp[N][j]jが大きいところから見ていって、初めてW以下になったときのjが答えなのだ!

rep(i,0,N+1)rep(j,0,100001)dp[i][j]=INF;
dp[0][0]=0;
rep(i,1,N+1)rep(j,0,100001){
	if(j-v[i]>=0)dp[i][j]=min(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
	else dp[i][j]=dp[i-1][j];//j-v[i]<0のときはi番目の品物は使えないのだ!
}
ans=100000;
while(dp[N][ans]>W)ans--;

jのループをどこまでする必要があるかといえば、全部の品物を選んだときの\sum v[i]\leq 10^5なのだ。だから計算量はO(N\sum v[i])なのだ。

……「これを思いつくのは無理」? 確かに難しいけど、次のどれかの発想を使って思いつくのだ!
・発想1:制約を見る
この問題で考えるべきものは「何番目の品物まで使ったか」「価値はいくらか」「重さはいくらか」の3つなのだ。制約を考えると、状態として持てるのは多くても前の2つだけなのだ。だから
dp[ 何番目][価値]=(i番目までの品物を、価値に関するなんらかの条件を満たすように選んだときの重さに関するなにか)
という形にするしかないのだ。あとは頑張って考えるのだ!
・発想2:boolのDP
まずはさっき言った「考えるべきもの3つ」を全部状態に持つDPを考えてみるのだ。
dp[i][j][k]=(i番目までの品物を、価値がj、重さがkとなるように選べるか?)
ここで「『はい』か『いいえ』を値に持つようなDPは、添字を1つ選んで「~が『はい』になる〇〇の最小値(最大値)」という形に書き直せることが多い」というテクニックを使うと、思いつけるのだ!
・発想3:添字の入れ替え
「~となる〇〇の最大値」を値に持つDPは、適当な添字と入れ替えて「~となる××の最小値」というDPにできることが多いのだ!
D問題の解法にこれを使えば思いつけるのだ!
これは発想2の考え方「~となる○○の最大値」→「~とできるか?」→「~となる××の最小値」の間を飛ばしたものとも言えるのだ。


これでアライさんの解説はおしまいなのだ。
アライさんにかかればこんなの朝飯前なのだ!
次のF問題からはサーバルが解説してくれるのだ。

EDPC解説目次へ戻る