効果の件名:
私が過ごすする方法がわからないジョンは、多くの場合、牛の高乳量のための特別な手当をした、とすぐに牛が多額の資金を持っています。このような理由から、ジョンは、購入した\(N(1 \ルN \ル2000)\) 牛を販売する美味しいスナックの部分を。ジョンは毎日軽食を販売しました。これらすべてのスナックは、最大の利益を得るために売却した後、ジョンは確かに願っています。これらのスナックは興味深い特徴を次のようしています。
- スナック(1 \ cdots N \)\長いボックスの行にある番号。ボックスは、両端に開口部を有する、ジョンは、一日あたりのボックスのいずれかの端部から除去することができる最も外側。
- ワインとおいしいチーズと同様に、長いこれらのスナックは、より美味しく保存します。もちろん、ヨハネは彼らに高い価格を販売することができそう。
- 各スナックの初期値は、必ずしも同じではありません。ときにジョン・購入、\(I \)スナックの一部として初期値\(V_I(1 \ルV_I \ル1000)\) 。
- 最初の\(I \)スナックの部分であれば、購入された後に\(A \)販売日、その価格は\(V_I \タイムズA \) 。
\(V_I \)私スナックの初期値のボックス部分の上から下です。これらのスナックは、すべて彼が最もお金を得ることができますどのくらい、販売された後、ジョンは、すべてあなたのスナックの初期値を告げた、とあなたは彼を計算することができます願っています。
問題解決のアイデア:
我々は、状態を定義する(F [L] [Rは\ ] \) 間隔である(\ [L、R] \ ) 最もお金を得ることができるように空き缶が続きます。
その後、我々は我々の答えをされて見つけることができます\(F [1] [N ] \) 、その後、どのように解決するために\(F [1] [N ] \) それ?ない心配する、〜慎重に持っている私に耳を傾けます
私たちは今のソリューションを必要とし(F [L] [R] \)\区間のため、我々は見つけることができ、\([L、R] \) 、我々はわずか2ソリューションの軽食を取ります:
- スキーム:左から除去\(V_L \)は、次いで状態となる\([1 + L] F [R&LT] \) 。
- オプションII:の右側から除去される\(V_R \) 、次いで状態となる(F [L] [R&LT-1] \)\。
間隔\([L、R] \) 、レッツ・メイクは確かに我々は(それがプログラムまたはOption IIであるかどうか)最初のいくつかのスナックを除去して軽食を奪いますか?
我々は、見ることができる\([L、R&LT] \)の左部である\(L-1 \)スナックの部分が除去される前に、正しい\(NR \)除去される前に、部品のスナック、そう我々は今、軽食を取るされている(L-1 + nRの+ \ 1 = N + LR \) の部分。
そのため、ほとんどのお金最初のオプションを使用すると、取得することです
\ [F [L + 1] [R] + V_L \回(N + LR)\]
2番目のオプションは、ほとんどのお金を得ることができる使用しています
\ [F [L] [R-1] + V_R \回(N + LR)\]
我々は、溶液状態が現在ことを必要とする(F [L] [Rは\ ] \) は以下のように2つの方式の大きな値でなければならない、我々は最終的な状態遷移方程式を得ることができることです。
\ [F [L] [R] = \ MAX(F [L + 1] [R] + V_L \回(N + LR)、F [L] [R-1] + V_R \回(N + LR) )\]
インターバルの長さ:もちろん、すなわち、その私たちの場合、境界条件、またに注意して\(1 \)の場合、この時点で、すべてのインターバル\([I、I] \) 、最初の\(I \)スナックの部分(すなわち、最終的に除去される\(N- \)ので、部品が除去されます)
\ [F [i]は[I] = V_I \ n倍\]
上記の導出に基づいて、我々は検索の形でコードの私達のメインメモリを実現することができます。
int dfs(int L, int R) {
if (f[L][R]) // 记忆化操作
return f[L][R];
if (L == R) // 边界条件
return V[L] * n;
return f[L][R] = max(dfs(L+1, R)+V[L]*(n+L-R), dfs(L, R-1)+V[R]*(n+L-R));
}
思考とメモリ検索はまだ人間の脳の思考の私達の方法に非常に対応している、私たちは、このプログラムは3つの部分に分かれていることがわかります。
私たちは、最初にdfs(L,R)
戻すことである\(F [L] [R]を\)そのように:
- まず、判定が既に算出されない(で[L] F(\ [R ] \) かどうかを\(0 \)決定される)、プレイの結果場合、直接計算されています。
- 第二に、境界条件は(によって決定されていない)(L \ \に等しい(R&LT \)\境界条件を直接返されるかどうかを決定する)(V_L \ n回\)\。
- 最後に、値は、次の時間が直接戻さ算出されるように、計算され記録されます。
使用メモリが完全なコードを検索し、次のとおりです。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2020;
int n, V[maxn], f[maxn][maxn];
int dfs(int L, int R) {
if (f[L][R]) // 记忆化操作
return f[L][R];
if (L == R) // 边界条件
return V[L] * n;
return f[L][R] = max(dfs(L+1, R)+V[L]*(n+L-R), dfs(L, R-1)+V[R]*(n+L-R));
}
int main() {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> V[i];
cout << dfs(1, n) << endl;
return 0;
}
我々はまた、この問題を解決するための一般的な形式を採用することができます(通常はアイデアの形での検索やメモリの形で同じですが、1ループのためにまっすぐに降りてくるされ、他は再帰的に来ている、と類推を区別します)。
我々は、次に、大間隔(即ち、間隔長より大きい部分)があれば、我々は小規模から大規模までの間隔の長さを横断するように、誘導された状態に対応する(すなわち、より小さな間隔長間隔)セル間に対応する状態によるものである見つけることができます対応する状態を計算するために、間隔を横断座標左。
次のようにメインのコードは次のようになります。
for (int l = 1; l <= n; l ++) { // 从小到大遍历区间长度l
for (int i = 1; i+l-1 <= n; i ++) { // 遍历区间左边界i
int j = i+l-1; // 通过左边界i和区间长度l获得区间右边界j
if (l == 1) f[i][j] = V[i]*n; // 边界条件直接返回结果
else f[i][j] = max(f[i+1][j] + V[i]*(n+i-j), f[i][j-1] + V[j]*(n+i-j)); // 否则,通过状态转移方程推导
}
}
次のように完全なコードの一般的な形式は以下のとおりです。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2020;
int n, V[maxn], f[maxn][maxn];
int main() {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> V[i];
for (int l = 1; l <= n; l ++) { // 从小到大遍历区间长度l
for (int i = 1; i+l-1 <= n; i ++) { // 遍历区间左边界i
int j = i+l-1; // 通过左边界i和区间长度l获得区间右边界j
if (l == 1) f[i][j] = V[i]*n; // 边界条件直接返回结果
else f[i][j] = max(f[i+1][j] + V[i]*(n+i-j), f[i][j-1] + V[j]*(n+i-j)); // 否则,通过状态转移方程推导
}
}
cout << f[1][n] << endl;
return 0;
}