Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions 1011/solution1_1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
・概要
自力解放。10分ほどで実装して、10分くらいでTLE

・解法
重さを固定して、毎回weightsを操作して条件にあうか確かめる。
重さの候補としては、weightsの最大値をmaxWとすると、maxW <= 候補 <= maxW * weightsの要素数
となるので、この範囲で探していき、条件通り指定日以下になるかみていく。

・計算量
// O(m * n), m: 最大値*weights, n: weightsの要素数
// 今回の条件的には mが最大で5 * 10 ^ 4 * 500 なので大きく見積もっても10 ^ 8

・所感
ギリギリまにあうかなとおもったけど、TLEになってしまい、改善できそうなところを考えると線形走査しているmaxW <= 候補 <= maxW * weightsの要素数の部分を二分探索すればlognに計算量を減らせるのでそちらは次の解法でためす。

*/

public class solution1_1 {
class Solution {
// weightsの最大値を見つける。
// 最大値から最大値*weightsの数の値だけ繰り返し処理をする
// 繰り返し処理:capを固定してweightsを走査して、指定日数以下で返せるかをみていく。
// O(m * n), m: 最大値*weights, n: weightsの要素数
// mは制約から最大で5 * 10 ^ 4 * 500 = 10 ^ 8

public int shipWithinDays(int[] weights, int days) {
int maxWeight = 0;
for (int weight : weights) {
maxWeight = Math.max(maxWeight, weight);
}

for (int capacity = maxWeight; capacity <= maxWeight * weights.length; capacity++) {
int currentSumWeights = 0;
int currentDays = 1;
for (int weight : weights) {
if (currentSumWeights + weight <= capacity) {
currentSumWeights += weight;
} else {
currentSumWeights = weight;
currentDays++;
if (days < currentDays) {
break;
}
}
Comment on lines +37 to +45
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

個人的にはここのif-elseはif-continueで書きたいと感じました。

Suggested change
if (currentSumWeights + weight <= capacity) {
currentSumWeights += weight;
} else {
currentSumWeights = weight;
currentDays++;
if (days < currentDays) {
break;
}
}
if (currentSumWeights + weight <= capacity) {
currentSumWeights += weight;
continue;
}
currentSumWeights = weight;
currentDays++;
if (days < currentDays) {
break;
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。たしかにネストへるしいいですね

}
if (currentDays <= days) {
return capacity;
}
}
return -1; // ここは想定外なのでエラーハンドリングなどは相談
}
}
}
60 changes: 60 additions & 0 deletions 1011/solution1_2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
・概要
solution1_1について走査部分を二分探索にして計算量を改善したもの。
15分くらいでAC

・解法
走査部分について
1, 不変条件
見つけたいもの:条件を満たす最小のcapacity
left: これを以下のCapacityでは条件をみたさない。
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これ未満のCapacityでは条件をみたさない。ですね

right:これ以上のCapacityで条件をみたす。
2, 更新処理
leftとrightの間の値candidateをみていく。条件(かかる日数がdays以下)を満たすの時は、
定義より、right = candidate
逆に条件を満たさない時は、left = candidate + 1;
3, 終了条件
定義より、leftとrightが一致したところが見つけたいものと同じになるのでleft == right

・計算量
// O(log(m) * n), m: 最大値*n, n: weightsの要素数
// m = 500n = 500 * 5 * 10 ^ 4 = 25 * 10 ^ 6 → 2 * 10 ^ 7
// nlog(m) = 500 * log(2 * 10 ^ 7) = 500 * 7 * log(20) = 3500 * log (20) = 3500 * (2log(2) + log(5)) = 3500 * (2 + 2.3) = 3500 * 5 = 175000 = 2 * 10 ^ 5
// 大体Javaが1sで10^7くらい処理ができるとすると、2 * 10 ^ 5 / 10 ^ 7 = 2 / 10 ^ 2 = 20msくらいで完了しそう
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分はJavaにそこまで詳しくないですが、1sで10^7くらい、はやや過小評価に感じました。

JITコンパイラ特有のスロースタート的な特性などもあると思いますが、このくらいの繰り返しが明確で単調、インスタンス生成も特にない処理なら、JIT最適化後は効率的に機械語に翻訳され1sに10^8-10^9くらい行くかなと思いました。(CPUのクロック周波数が数GHz程度という前提のもと)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ベンチマークサイトによると Java は C に比べて 3~4 倍程度遅いようです。

1s に 107~108 くらいで見積もるとよいと思いました。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

私も 10^8-10^9 くらいで見積もりそうですが、しかし、一般にソフトウェアの速度は保守的に見積もったほうがいいんですよね。(予想外に速くて困ることはあまりないが逆は困ることがあるので。)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。そうですね、少し幅を 10^7~10^8 くらいで認識します。


*/

public class solution1_2 {
public int shipWithinDays(int[] weights, int days) {
int maxWeight = 0;
for (int weight : weights) {
maxWeight = Math.max(maxWeight, weight);
}
int left = maxWeight;
int right = maxWeight * weights.length;
while (left < right) {
int candidateCapacity = left + (right - left) / 2;
int currentSumWeights = 0;
int currentDays = 1;

// この辺りは関数にしてもいいかも
for (int weight : weights) {
if (currentSumWeights + weight <= candidateCapacity) {
currentSumWeights += weight;
} else {
currentSumWeights = weight;
currentDays++;
if (days < currentDays) {
break;
}
}
}
if (currentDays <= days) {
right = candidateCapacity;
} else {
left = candidateCapacity + 1;
}
}
return left;
}
}
53 changes: 53 additions & 0 deletions 1011/solution2_1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
・概要
他の人のPRなどを参考にしたもの。
https://github.com/garunitule/coding_practice/pull/44/files
変数のつけかたが具体的でいい。自分はsolution1_2で二分探索のところを安易にleft, rightと置いていたが確かに処理フロー的に不自然なのでコンテキストにあった名前にしたい
ex) left = maxInvalidCapacity, right = minValidCapacity とか? もっと簡単なら minCapacity, maxCapacityとかでもよいかも?
あと、solution1_2でも思った通り、条件にあうかの判定部分を関数化しているのでこれもよさそう

https://github.com/h1rosaka/arai60/pull/46/files
走査の最大値が配列の合計値という違いがある(たしかにこれはそう)。終了条件と定義からleft,rightが隣り合っている時となっている。

https://github.com/hayashi-ay/leetcode/pull/55/files#diff-4e146417f14c744a10f851601f26cd2cb17b420ff966720e568f6f5679aa475eR36-R58
の2ndはきれい
*/

public class solution2_1 {
public int shipWithinDays(int[] weights, int days) {
int maxWeight = 0;
int sumWeight = 0;
for (int weight : weights) {
maxWeight = Math.max(maxWeight, weight);
sumWeight += weight;
}
int minCapacity = maxWeight;
int maxCapacity = sumWeight;
while (minCapacity < maxCapacity) {
int midCapacity = minCapacity + (maxCapacity - minCapacity) / 2;
if (canShipWeight(weights, days, midCapacity)) {
maxCapacity = midCapacity;
} else {
minCapacity = midCapacity + 1;
}
}
return minCapacity;
}

private boolean canShipWeight(int[] weights, int days, int capacity) {
int currentSumWeight = 0;
int currentDays = 1;
for (int weight : weights) {
if (currentSumWeight + weight <= capacity) {
currentSumWeight += weight;
} else {
currentSumWeight = weight;
currentDays++;
}
if (days < currentDays) {
return false;
}
}
return true;
}
}