私は最初の年のコンピュータ科学の学生だと私は現在、いくつかのアルゴリズムの大会で手を染めています。私が作ったことを以下のコードは、私が修正するかどうかはわかりませんという欠陥を持っています
ここでは、問題文は次のとおりです。http://www.usaco.org/index.php?page=viewproblem2&cpid=811
それは農夫ジョンが唯一、両方のブーツで立つことができるというのタイルの上にブーツを切り替えることができると言ったところ声明の中で、私は逃しました。私は別の場所に制約を追加しようとしたが、どれも完全に問題を解決するように見えません。私は実際にコードを解体せずにそれを行うための方法が表示されません
基本的に、問題はジョンが新しいブーツで立つことができないタイルの上にブーツを切り替える保持していることであり、私はそれを修正するように見えることはできません
ここに私のコードは、(1文字の変数のため申し訳ありません)です。
import java.io.*;
import java.util.*;
public class snowboots {
static int n,k;
static int[] field,a,b; //a,b --> strength, distance
static int pos;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("snowboots.in"));
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("snowboots.out")));
StringTokenizer st = new StringTokenizer(br.readLine());
n = Integer.parseInt(st.nextToken());
k = Integer.parseInt(st.nextToken());
st = new StringTokenizer(br.readLine());
field = new int[n];
a = new int[k];
b = new int[k];
for (int i = 0; i < n; i++)
field[i] = Integer.parseInt(st.nextToken());
for (int i = 0; i < k; i++) {
st = new StringTokenizer(br.readLine());
a[i] = Integer.parseInt(st.nextToken());
b[i] = Integer.parseInt(st.nextToken());
}
pw.println(solve());
pw.close();
}
static int solve() {
pos = 0;
int i = 0; //which boot are we on?
while(pos < n-1) {
while(move(i)); //move with boot i as far as possible
i++; //use the next boot
}
i--;
return i;
}
static boolean move(int c) {
for (int i = pos+b[c]; i > pos; i--) {
if (i < n && field[i] <= a[c]) { //snow has to be less than boot strength
pos = i;
return true;
}
}
return false;
}
}
私は私の更新時に「移動」する方法、および1つに制約を加えることを試みたが、彼らの両方が不要な時期に厳しすぎると活性化します
それが引き揚げますか?
はい、それは余分に追加することで、あなたのソリューションをサルベージすることが可能ですfor
-loopを。
何をする必要があなたはブーツの以前のペアがあなたにあまりにも深くあなたの次のペアのために雪の中だタイルにすべての方法を得ることができることを発見した場合、その後、あなたはだ最新のタイルに「バックトラッキング」を試してみる必要がありませんあまりにも深い。最悪の場合には解決策を与えてまで、この両端O(N・B)の時間とO(1)余分なスペース。
すべての後に、ちょうどあなたは、必ずしもあなたがその前にすべてのタイルを達することができたという意味ではありませんので、所与のタイルに到達することができますので、 - -ので、私は少し説明させて、そのタイルにバックトラックに、それのOKは、理由は明らかではないかもしれませんなぜそれがある OK。
させるmaxReachableTileNum
(1との間の数値N、あなたの前のブーツで到達することができましたことを最後のタイルの)、とlet lastTileNumThatsNotTooDeep
(1との間の数値Nのか、前の最後のタイルの)maxReachableTileNum
あまりにも深く雪に覆われたではありませんあなたの次のペアのために。(私たちはそこにいることを知っている今、我々は得ることができましたので、他に何も我々は非常に始まりに引き返すことができることを知っていない場合、タイルは#1がそう、全く雪がないので、そのようなタイル)はmaxReachableTileNum
、その後、いくつかの以前のブートいずれかを踏んでいなければなりませんlastTileNumThatsNotTooDeep
(その場合には、何の問題は、それが到達ません)または(上または前にいくつかの後のタイルに飛ばしmaxReachableTileNum
)。しかし、その後タイルはより深くなければならないことlastTileNumThatsNotTooDeep
(それ以降のタイルの深さがより大きいので、ScurrentBootNum
の深さと偉大で少なくともある、lastTileNumThatsNotTooDeep
スキップブートことを意味し、)lastTileNumThatsNotTooDeep
確かに可能性を踏んできたlastTileNumThatsNotTooDeep
代わりに:それは上に短いステップ(OK)を服用意味しているだろうそれが実際に何をしたか未満-深く覆われたタイル(OK)。だから、どちらかの方法は、我々はそれが知っているlastTileNumThatsNotTooDeep
到達可能でした。それは安全ですので、私たちのためにバックトラックしようとしますlastTileNumThatsNotTooDeep
。(注:以下のコードは、名前を使用するreachableTileNum
代わりにlastTileNumThatsNotTooDeep
、それは使用し続けているため、reachableTileNum
到達可能なタイルを見つけるために、前方に検索するための変数を。)
しかし、我々はまだ以前の上に保持する必要がmaxReachableTileNum
(それは私たちがすでに持っているよりも、更なる前進を作ってみようではないかもしれないので)私たちはこれらのブーツを破棄します。その場合には、有用ではないと判明する可能性があるバックトラック、および移動:次のペアへの、とmaxReachableTileNum
その前の値で。
だから、全体的に、我々はこれを持っています:
public static int solve(
final int[] tileSnowDepths, // tileSnowDepths[0] is f_1
final int[] bootAllowedDepths, // bootAllowedDepths[0] is s_1
final int[] bootAllowedTilesPerStep // bootAllowedTilesPerStep[0] is d_1
) {
final int numTiles = tileSnowDepths.length;
final int numBoots = bootAllowedDepths.length;
assert numBoots == bootAllowedTilesPerStep.length;
int maxReachableTileNum = 1; // can reach tile #1 even without boots
for (int bootNum = 1; bootNum <= numBoots; ++bootNum) {
final int allowedDepth = bootAllowedDepths[bootNum-1];
final int allowedTilesPerStep = bootAllowedTilesPerStep[bootNum-1];
// Find the starting-point for this boot -- ideally the last tile
// reachable so far, but may need to "backtrack" if that tile is too
// deep; see explanation above of why it's safe to assume that we
// can backtrack to the latest not-too-deep tile:
int reachableTileNum = maxReachableTileNum;
while (tileSnowDepths[reachableTileNum-1] > allowedDepth) {
--reachableTileNum;
}
// Now see how far we can go, updating both maxReachableTileNum and
// reachableTileNum when we successfully reach new tiles:
for (int tileNumToTry = maxReachableTileNum + 1;
tileNumToTry <= numTiles
&& tileNumToTry <= reachableTileNum + allowedTilesPerStep;
++tileNumToTry
) {
if (tileSnowDepths[tileNumToTry-1] <= allowedDepth) {
maxReachableTileNum = reachableTileNum = tileNumToTry;
}
}
// If we've made it to the last tile, then yay, we're done:
if (maxReachableTileNum == numTiles) {
return bootNum - 1; // had to discard this many boots to get here
}
}
throw new IllegalArgumentException("Couldn't reach last tile with any boot");
}
(私はUSACOのデータ例でこれをテストし、それが返さ2
予想通り、。)
これは、潜在的な位置を追跡するために(彼らは強くもより機敏前回成功したペアよりもないだから)、または余分なデータ構造を明確に役立たないブーツのペアをスキップするロジックで例えば、さらに最適化することができます(バックトラッキングプロセスを最適化するために)最新最小の、又はおそらく有益であるよりもバックトラックを回避するためのロジックと; しかし、その与えられたN・B ≤250 2 = 62500、私はどのような最適化が保証されているとは思いません。
追加編集した(2019年2月23日)を:私はこれをさらにについて考えてきた、そして最悪のケースの中にソリューションを書くことが実際に可能だと私に発生したO(N + B ログ N漸近的に優れている)時間(よりO(N・B))とO(N)余分なスペース。しかし、それはだずっともっと複雑。それは1つが中にバックトラックできるように、最新の最小値の位置を追跡するために(3余分なデータ構造を伴うO(ログ Nを)時間; 1は、の位置を追跡するために、将来の中でチェックできるように、最小値O(ログ Nバックトラックは、(関連最小限に前進し、そうであれば)、実際に人がいる場合の時間)と、一つは、第二のものがで維持させるために必要な前向きな情報を維持するために償却 O(1)時間)。それもその溶液を内にあることが保証されている理由を説明するには複雑だO(N + B ログ N)時間(それは多くの伴うため、償却分析を、最適化のように見えるかもしれませんがマイナーチェンジすること-などを、線形探索を置き換えますバイナリ検索で-分析を破ると、実際にでき増やす最悪の場合の時間の複雑さを導入されたNとBは、両方の最も250であることが知られている、私はすべての合併症はそれだけの価値があるとは思いません。