Problem:
問題 6: 小籠包 (Xiao Long Bao)

問題

JOI 君はお昼ごはんに，中華料理屋で小籠包を食べることにした．
小籠包とは，具と熱いスープを小麦粉の皮で包んだ料理であり，食べるときにスープが周囲に飛び散ることで知られている．

JOI 君が注文した小籠包のセットは，具やスープの異なる N 個の小籠包からなる．
N 個の小籠包は等間隔に一列に並んでおり，順番に 1 から N の番号がつけられている．
i 番目の小籠包と j 番目の小籠包の間の距離は絶対値 |i - j| である．

JOI 君は小籠包をある順番で食べていく．
最初，すべての小籠包のおいしさは 0 である．
i 番目の小籠包を食べると，周囲にその汁が飛び散り，まだ食べられていない小籠包のうち，小籠包 i からの距離が Di 以下の小籠包に汁がかかる．汁がかかった小籠包はおいしさが
Ai 増える．すなわち， i 番目の小籠包を食べたときに，
j 番目の小籠包 (1 ≤ j ≤ N かつ i - Di ≤ j ≤ i + Di) がまだ食べられずに残っているならば，
j 番目の小籠包のおいしさが Ai 増える．

JOI 君は，食べる順番を工夫することで，食べる小籠包のおいしさの合計を最大化したい．
もっとも良い順番で食べたときの，JOI 君が食べる小籠包のおいしさの合計を求めるプログラムを作成せよ．

入力

入力ファイルは 3 行からなる．

1 行目には 1 つの整数 N (1 ≤ N ≤ 100) が書かれている．

2 行目には， N 個の整数 D1, D2, ..., DN (0 ≤ Di ≤ 7) が空白を区切りとして書かれている．

3 行目には， N 個の整数 A1, A2, ..., AN (0 ≤ Ai ≤ 1000) が空白を区切りとして書かれている．

出力

JOI 君が食べる小籠包のおいしさの合計の最大値を 1 行で出力せよ．

入出力例

入力例 1

5
1 0 1 1 2
0 2 6 3 4

出力例 1

20

入出力例 1 では，5 番目 → 3 番目 → 1 番目 → 2 番目 → 4 番目 の順番で食べると，
おいしさの合計が 20 になる．合計が 20 を超えるような食べ方は存在しないので，これが最善である．

入力例 2

10
5 2 7 2 6 5 3 5 3 6
8 7 8 4 0 6 0 10 10 0

出力例 2

237

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in C++11 (judge verdict: Accepted)

#include<bits/stdc++.h>

#define REP(i,s,n) for(int i=s;i<n;++i)
#define rep(i,n) REP(i,0,n)
#define ALL(x) x.begin(),x.end()

using namespace std;

#define MAX 110
int n,D[MAX],A[MAX];
map<vector<int>,int> dp[MAX];
map<vector<int>,int> score[MAX];

void normalize(vector<int> &vec) {
  vector<int> tmp = vec;
  sort(ALL(tmp));
  rep(i,(int)vec.size()) vec[i] = lower_bound(ALL(tmp),vec[i]) - tmp.begin();
}

void init(int Dmax) {
  vector<int> init;
  rep(i,min(Dmax+1,n)) init.push_back(i);
  do {
    int c_score = 0;
    vector<int> nex;
    rep(i,(int)init.size()) {
      if( i && i <= D[0] && init[0] < init[i] ) c_score += A[0]; // 0 ????????????
      if( i && i <= D[i] && init[0] > init[i] ) c_score += A[i]; // 0 ????????????
      if( i ) nex.push_back(init[i]);
    }
    normalize(nex);
    dp[0][nex] = max(dp[0][nex],c_score);
  } while(next_permutation(ALL(init)));
}


struct Data {
  int key, ID;
  bool operator < ( const Data & data ) const {
    return key < data.key;
  }
};

void compute() {
  int Dmax = 0;
  rep(i,n) Dmax = max(Dmax,D[i]);

  init(Dmax);

  int maxi = 0;
  REP(i,1,n) {
    if( i + Dmax < n ) {
      for(auto v : dp[i-1]) {
	vector<int> cur = v.first;
	int c_score = v.second;
	rep(j,(int)cur.size()) {
	  cur[j] += 1;
	  cur[j] *= 10;
	}

	int n_score = c_score;
	vector<int> nex;
	cur.push_back(5);

	rep(j,(int)cur.size()) {
	  if( j && j <= D[i+0] && cur[0] < cur[j] ) n_score += A[i+0];
	  if( j && j <= D[i+j] && cur[0] > cur[j] ) n_score += A[i+j];
	  if( j ) nex.push_back(cur[j]);
	}
	normalize(nex);
	dp[i][nex] = max(dp[i][nex],n_score);

	rep(_,Dmax) {
	  nex.clear();
	  n_score = c_score;
	  cur.back() += 10;

	  rep(k,(int)cur.size()) {
	    if( k && k <= D[i+0] && cur[0] < cur[k] ) n_score += A[i+0];
	    if( k && k <= D[i+k] && cur[0] > cur[k] ) n_score += A[i+k];
	    if( k ) nex.push_back(cur[k]);
	  }
	  normalize(nex);
	  dp[i][nex] = max(dp[i][nex],n_score);
	}
      } 
    } else {
      for(auto v : dp[i-1]) {
	vector<int> cur = v.first;
	int c_score = v.second;
	rep(j,(int)cur.size()) {
	  REP(k,j+1,(int)cur.size()) {
	    if( k-j <= D[i+j] && cur[j] < cur[k] ) c_score += A[i+j];
	    if( k-j <= D[i+k] && cur[j] > cur[k] ) c_score += A[i+k];
	  }
	}
	maxi = max(maxi,c_score);
      }
      break;
    }
  }
  cout << maxi << endl;
}

int main() {
  cin >> n;
  rep(i,n) cin >> D[i];
  rep(i,n) cin >> A[i];
  compute();
  return 0;
}