第一题
leetcode296. 最佳的碰头地点
public class 第一题12 {
public static void main(String[] args) {
int[][] grid = {{1,1,0,1},{1,1,0,0},{1,0,0,0},{1,1,1,1}};
int res = minTotalDistance(grid);
System.out.println(res);
}
public static int minTotalDistance(int[][] grid){
int n = grid.length;
int m = grid[0].length;
int[] rowOnes = new int[n];
int[] colOnes = new int[m];
for (int i = 0;i < n;i++){
for (int j = 0;j < m;j++){
if (grid[i][j] == 1){
rowOnes[i]++;
colOnes[j]++;
}
}
}
int total = 0;
int up_row = 0;
int down_row = 0;
int up = 0;
int down = n - 1;
while (up < down){
if (rowOnes[up] + up_row < rowOnes[down] + down_row){
total += rowOnes[up] + up_row;
up_row += rowOnes[up++];
}else {
total += rowOnes[down] + down_row;
down_row += rowOnes[down--];
}
}
int left_col = 0;
int right_col = 0;
int left = 0;
int right = m - 1;
while (left < right){
if (colOnes[left] + left_col < rowOnes[right] + right_col){
total += colOnes[left] + left_col;
left_col += colOnes[left++];
}else {
total += colOnes[right] + right_col;
right_col += colOnes[right--];
}
}
return total;
}
}
思路:
1.递归——》记忆化搜索
2.贪心 + 后缀和 + 二分(在sum非常大时,记忆化搜索效果很差)
public class 第二题12 {
public static void main(String[] args) {
int[] arr = {3,4,1,5,3,4,6,7,3,6,1,3,4,6,9,3,2};
int res1 = minOpSteps1(arr, 2, 5);
System.out.println(res1);
int res2 = minOpSteps2(arr, 2, 5);
System.out.println(res2);
int res3 = minOpSteps3(arr, 2, 5);
System.out.println(res3);
}
//暴力递归
public static int minOpSteps1(int[] arr,int x,int y){
int sum = 0;
for (int i = 0;i < arr.length;i++){
sum += arr[i];
}
return process1(arr,x,y,0,sum);
}
private static int process1(int[] arr, int x, int y, int index, int sum) {
if (sum <= 0){
return 0;
}
if (index == arr.length){
return Integer.MAX_VALUE;
}
int p1 = process1(arr,x,y,index + 1,sum);//第一种情况:什么也不做
int p2 = Integer.MAX_VALUE;//第二种情况:执行 x 操作
int next_p2 = process1(arr,x,y,index + 1,sum - arr[index]);
if (next_p2 != Integer.MAX_VALUE){
p2 = next_p2 + x;
}
int p3 = Integer.MAX_VALUE;//第三种情况:执行y操作
int next_p3 = process1(arr,x,y,index + 1,sum - 2 * arr[index]);
if (next_p3 != Integer.MAX_VALUE){
p3 = y + next_p3;
}
return Math.min(p1,Math.min(p2,p3));
}
//记忆化搜索
public static int minOpSteps2(int[] arr,int x,int y){
int sum = 0;
for (int i = 0;i < arr.length;i++){
sum += arr[i];
}
int[][] dp = new int[arr.length + 1][sum + 1];
for (int i = 0;i <= arr.length;i++){
for (int j = 0;j <= sum;j++){
dp[i][j] = -1;
}
}
return process2(arr,x,y,0,sum,dp);
}
private static int process2(int[] arr, int x, int y, int index, int sum, int[][] dp) {
if (sum <= 0){
return 0;
}
if (index == arr.length){
return Integer.MAX_VALUE;
}
if (dp[index][sum] != -1){
return dp[index][sum];
}
int p1 = process2(arr,x,y,index + 1,sum, dp);//第一种情况:什么也不做
int p2 = Integer.MAX_VALUE;//第二种情况:执行 x 操作
int next_p2 = process2(arr,x,y,index + 1,sum - arr[index], dp);
if (next_p2 != Integer.MAX_VALUE){
p2 = next_p2 + x;
}
int p3 = Integer.MAX_VALUE;//第三种情况:执行y操作
int next_p3 = process2(arr,x,y,index + 1,sum - 2 * arr[index], dp);
if (next_p3 != Integer.MAX_VALUE){
p3 = y + next_p3;
}
dp[index][sum] = Math.min(p1,Math.min(p2,p3));
return dp[index][sum];
}
//贪心(当sum很大的时候,记忆化搜索效果就很差了)
public static int minOpSteps3(int[] arr,int x,int y){
int n = arr.length;
Arrays.sort(arr);
for (int i = 0,j = n - 1;i <= j;i++,j--){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
if (x >= y){
int sum = 0;
int ans = 0;
for (int i = 0;i < n;i++){
sum += arr[i];
}
for (int i = 0;i < n && sum > 0;i++){
sum -= 2 * arr[i];
ans += y;
}
return ans;
}else {
for (int i = n - 2;i >= 0;i--){
arr[i] += arr[i + 1];
}
int benefit = 0;
int left = mostLeft(arr,0,benefit);// y 为0个时的代价
int cost = left * x;
for (int i = 0;i < n - 1;i++){
benefit += arr[i] - arr[i + 1];
left = mostLeft(arr,i + 1,benefit);
cost = Math.min(cost,(left - i - 1) * x + (i + 1) * y);
}
return cost;
}
}
private static int mostLeft(int[] arr, int l, int benefit) {
int r = arr.length - 1;
int last = arr.length;
while (l <= r){
int mid = (l + r) / 2;
if (arr[mid] <= benefit){
last = mid;
r = mid - 1;
}else {
l = mid + 1;
}
}
return last;
}
}