blogskol

思わず声に出して読みたくなるブログ

AGC050-B Three Coins

B - Three Coins

解法

区間dpをすると良さそう

dp[l][r][0]:コインの無い区間[l,r)を渡された時の最大値
dp[l][r][1]:コインだけの区間[l,r)を渡された時の最大値

遷移

1.r-l \lt 3

操作が出来ないのでそのまま

dp[l][r][0]=0
dp[l][r][1]=v[l..r]

2.r-l \geq 3

2.1r-lが3で割り切れない

区間の分割を全部試す

dp[l][r][0]= max_{l \lt i \lt r} (dp[l][i][0]+dp[i][r][0])
dp[l][r][1]= max_{l \lt i \lt r} (dp[l][i][1]+dp[i][r][1])

2.2r-lが3で割り切れる

2.1に加えて区間上のコインを反転したものも考える

dp[l][r][0]=dp[l][r][1]= \max_{l \lt i \lt r} \max_{c=0,1} (dp[l][i][c]+dp[i][r][c])


実装

再帰で書いたらTLEしたので区間の長さが短い順に求める形で書いた

#include <bits/stdc++.h>
using namespace std;

#define REP(i,n) for(int i=0;i<(n);i++)

const int INF=1e9;
int dp[505][505][2],n,v[505];
//dp[l][r][c]:区間[l,r),(c=1?全部コイン:コイン無し)の状態で渡された時の最大値

template<typename T>
void chmax(T &a,T b){
  if(a<b)a=b;
}

int main(){
  cin>>n;
  REP(i,n)cin>>v[i];
  
  for(int d=1;d<=n;d++)//区間の幅
    for(int l=0,r=d;r<=n;l++,r++)//[l,r)を調べる r=l+d
      REP(c,2)//コインあるか
        if(d<3)for(int i=l;i<r;i++)dp[l][r][c]+=v[i]*c;//幅が3未満なら操作出来ない
        else{
          dp[l][r][c]=-INF;
          for(int i=l+1;i<r;i++)chmax(dp[l][r][c],dp[l][i][c]+dp[i][r][c]);//どこで分けるか
          if((r-l)%3==0)//区間が3の倍数ならコインの状態反転させられる
            for(int i=l+1;i<r;i++)chmax(dp[l][r][c],dp[l][i][c^1]+dp[i][r][c^1]);
        }
        
  cout<<dp[0][n][0]<<endl;
}

atcoder.jp