場合分けが大変な問題。
https://yukicoder.me/problems/no/2553
問題
N日分のカレンダーがある。
いくつかの日は平日固定または休日に固定されている。
残りの日のうち、M日を休日にできる。また、固定されていない日のうち、前後が休日の日は、休日になる。
最適な日を休日にした時、最大何日休日にできるか。
解法
選択可能な連続した日に対し、
- 前後の休日の数
- 日数の偶奇
で分担しよう。
1日休日を設定することで、極力多く休日を増やせるようにする。
以下貪欲に行おう。
- 前後が休日で、選択可能な日が奇数の区間を処理する。
- 区間が短い順に、1日おきに休日を設定しよう。休日の設定数×2以上休日を増やせる。
- 以下を同様に処理する。
- 前後が休日で、選択可能な日が偶数の区間を処理する。
- 前後いずれかが休日の区間を処理する。
- 基本的に1日休日を設定すると、休日を2日増やせる。後者は区間が奇数の場合1日余るが、これはいったん残しておく。
- 以下を同様に処理する。
- 前後が平日の区間を処理する。
- 区間が長い順に、端に休日を設定してそこから1日おきに休日を設定する。
これでもまだ休日に設定可能な日数が残っているなら、残った選択可能な日を休日にしよう。
int N,M; string S; void solve() { int i,j,k,l,r,x,y; string s; cin>>N>>M>>S; vector<int> V; int ret=0; FOR(i,N) { if(S[i]!='-') V.push_back(i); if(S[i]=='o') ret++; } vector<int> C[3][2]; FOR(i,V.size()-1) if(V[i+1]-V[i]>1) { C[(S[V[i]]=='o')+(S[V[i+1]]=='o')][(V[i+1]-V[i]-1)%2].push_back(V[i+1]-V[i]-1); } FOR(x,3) FOR(y,2) sort(ALL(C[x][y])); FORR(v,C[2][1]) { if(M>=v/2) { M-=v/2; ret+=v; } else { ret+=M*2; M=0; } } vector<int> CC; FORR(v,C[2][0]) CC.push_back(v); FORR(v,C[1][1]) CC.push_back(v); FORR(v,C[1][0]) CC.push_back(v); FORR(v,CC) { if(M>=v/2) { M-=v/2; ret+=v/2*2; v%=2; } else { ret+=M*2; M=0; } } vector<int> DD; FORR(v,C[0][1]) DD.push_back(v); FORR(v,C[0][0]) DD.push_back(v); sort(ALL(DD)); reverse(ALL(DD)); FORR(v,DD) { if(M>=(v+1)/2) { M-=(v+1)/2; ret+=(v+1)/2*2-1; v=(v+1)%2; } else if(M) { ret+=M*2-1; M=0; } } FORR(v,CC) if(M&&v) ret++, M--; FORR(v,DD) if(M&&v) ret++, M--; cout<<ret<<endl; }
まとめ
こういう貪欲、一旦変にはまると解消が難しそう。