小美希望两个人吃的部分的美味度之和尽可能接近,请你输出
的最小值。(其中
代表小美吃的美味度,
代表小团吃的美味度)。
请务必保证,切下来的区域都是完整的,即不能把某个小正方形切成两个小区域。
第一行输出两个正整数和
,代表蛋糕区域的行数和列数。
接下来的行,每行输入
个正整数
,用来表示每个区域的美味度。
![]()
一个整数,代表的最小值。
2 3 1 1 4 5 1 4
0
把蛋糕像这样切开:
1 1 | 4
5 1 | 4
左边蛋糕美味度之和是8
右边蛋糕美味度之和是8
所以答案是0。
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[][] arr = new int[n][m];
long[] sumRow = new long[n]; // 每行所有元素相加的和
long[] sumCol = new long[m]; // 每列所有元素相加的和
for (int i = 0; i < n; i ++) {
for (int j = 0; j < m; j ++) {
arr[i][j] = sc.nextInt();
sumRow[i] += arr[i][j];
sumCol[j] += arr[i][j];
}
}
if (m == 1 && n == 1) {
System.out.println(arr[0][0]);
return;
}
/*
因为只能一刀,且不能切开每个元素,所以只能横一刀或者竖一刀
以行的最小差值为例,可以把分成上下两部分,分别从最上面和最小面开始累加,哪个小,就让哪个多加一次
*/
// 先计算行最小差值绝对值
int i = 0;
int j = n - 1;
long sumUp = sumRow[i];
long sumDown = sumRow[j];
while (i < j - 1) {
if (sumUp > sumDown) {
j--;
sumDown += sumRow[j];
} else {
i++;
sumUp += sumRow[i];
}
}
// 再计算列最小差值绝对值
i = 0;
j = m - 1;
long sumLeft = sumCol[i];
long sumRight = sumCol[j];
while (i < j - 1) {
if (sumLeft > sumRight) {
j --;
sumRight += sumCol[j];
} else {
i ++;
sumLeft += sumCol[i];
}
}
System.out.println(Math.min(Math.abs(sumDown - sumUp), Math.abs(sumLeft - sumRight)));
}
} #include <iostream>
#include <vector>
using namespace std;
long long fun (vector<vector<int>>& arr, int n, int m) {
// 求二维矩阵前缀和 之后的每一刀相当于求每个区间的美味度
vector<vector<long long>> arr_b(n + 1, vector<long long>(m + 1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
arr_b[i][j] = arr_b[i - 1][j] + arr_b[i][j - 1] - arr_b[i - 1][j - 1] + arr[i][j];
}
}
long long s1, s2;
long long min_result = arr_b[n][m];
// 横切
for (int i = 1; i < n; i++) {
s1 = arr_b[i][m];
s2 = arr_b[n][m] - s1;
if (abs(s1 - s2) < min_result) min_result = abs(s1 - s2);
}
// 竖切
for (int j = 1; j < m; j++) {
s1 = arr_b[n][j];
s2 = arr_b[n][m] - s1;
if (abs(s1 - s2) < min_result) min_result = abs(s1 - s2);
}
return min_result;
}
int main () {
int n, m;
scanf("%d %d", &n, &m);
vector<vector<int>> arr(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
scanf("%d", &arr[i][j]);
}
}
long long result = fun(arr, n, m);
printf("%lld", result);
return 0;
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
int n=in.nextInt();
long[]row=new long[n];
int m=in.nextInt();
long[]col=new long[m];
int[][]matrix=new int[n][m];
for (int i = 0; i < n; i++) {
if (i>0) row[i]=row[i-1];
for (int j = 0; j < m; j++) {
matrix[i][j]=in.nextInt();
row[i]+=matrix[i][j];
}
}
long sum=row[n-1];
for (int i = 0; i < m; i++) {
if (i>0) col[i]=col[i-1];
for (int j = 0; j < n; j++) {
col[i]+=matrix[j][i];
}
}
long ans=Long.MAX_VALUE;
for (long a:row){
ans=Math.min(ans,Math.abs(sum-a*2));
}
for (long a:col){
ans=Math.min(ans,Math.abs(sum-a*2));
}
System.out.println(ans);
}
} #include <iostream>
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
int n, m, tmp;
cin >> n >> m;
vector<vector<int64_t>> grid(n, vector<int64_t>(m));
int64_t sum_of_all = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> tmp;
grid[i][j] = tmp;
sum_of_all += tmp;
}
}
vector<int64_t> col_sum(m), row_sum(n);
for (int i = 0; i < n; ++i) {
auto& row = grid[i];
row_sum[i] = std::accumulate(row.begin(), row.end(), 0LL);
}
for (int j = 0; j < m; ++j) {
int64_t local_sum = 0;
for (int i = 0; i < n; ++i) {
local_sum += grid[i][j];
}
col_sum[j] = local_sum;
}
// 目标是找到最接近target的
int64_t ans = INT64_MAX;
// 首先竖着切分
{
auto get_left_sum = [&] (int mid) {
int64_t left_sum = 0;
for (int i = 0; i < mid; ++i) {
left_sum += col_sum[i];
}
return left_sum;
};
int left = 0, right = m - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
int64_t left_sum = get_left_sum(mid);
ans = min(ans, abs(sum_of_all - 2 * left_sum));
if (left_sum * 2 == sum_of_all) {
cout << 0 << endl;
return 0;
} else if (left_sum * 2 > sum_of_all) {
right = mid - 1;
} else {
left = mid + 1;
}
}
}
if (ans == 0) {
cout << 0 << endl;
return 0;
}
// 然后横着切分
{
auto get_up_sum = [&] (int mid) {
int64_t up_sum = 0;
for (int i = 0; i < mid; ++i) {
up_sum += row_sum[i];
}
return up_sum;
};
int up = 0, down = n - 1;
while (up <= down) {
int mid = up + (down - up) / 2;
int64_t up_sum = get_up_sum(mid);
ans = min(ans, abs(sum_of_all - 2 * up_sum));
if (up_sum * 2 == sum_of_all) {
cout << 0 << endl;
return 0;
} else if (up_sum * 2 > sum_of_all) {
down = mid - 1;
} else {
up = mid + 1;
}
}
}
cout << ans << endl;
return 0;
} 方法2:前缀和 #include <iostream>
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
int n, m;
int64_t sum = 0;
cin >> n >> m;
vector<vector<int64_t>> grid(n, vector<int64_t>(m));
vector<vector<int64_t>> presum(n, vector<int64_t>(m));
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
{
cin >> grid[i][j];
sum += grid[i][j];
}
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
{
if (i == 0 && j == 0)
presum[0][0] = grid[0][0];
else if (i == 0)
presum[0][j] = grid[0][j] + presum[0][j - 1];
else if (j == 0)
presum[i][0] = grid[i][0] + presum[i - 1][0];
else
presum[i][j] = grid[i][j] + presum[i][j - 1] + presum[i - 1][j] - presum[i - 1][j - 1];
}
assert(presum[n - 1][m - 1] == sum);
int64_t ans = INT64_MAX;
for (int i = 0; i < n; ++i)
ans = min<int64_t>(ans, abs(sum - 2 * presum[i][m - 1]));
for (int j = 0; j < m; ++j)
ans = min<int64_t>(ans, abs(sum - 2 * presum[n - 1][j]));
cout << ans << endl;
} h, w = [int(n) for n in input().split()] m = [] for i in range(h): m.append([int(n) for n in input().split()]) h_sum = [sum([m_[i] for m_ in m]) for i in range(w)] w_sum = [sum(m[i]) for i in range(h)] max = sum(w_sum) for i in range(1, w): part1_sum = sum(h_sum[:i]) part2_sum = sum(h_sum[i:]) sub = abs(part1_sum - part2_sum) if sub < max: max = sub for i in range(1, h): part1_sum = sum(w_sum[:i]) part2_sum = sum(w_sum[i:]) sub = abs(part1_sum - part2_sum) if sub < max: max = sub print(max)
一开始想用golang结果超时,同样的方法放到C++就通过了……还得是C++
用C++需要注意:输入的数据用int确实没问题,但求和的时候如果还用int的话会溢出导致WA,建议除了代表地址的n,m,i,j之外无脑long long。
#include <iostream>
#include <algorithm>
long long matrix[1000][1000];
long long sumByLine[1000];
long long sumByColumn[1000];
long long prefixByLine[1000];
long long prefixByColumn[1000];
long long distanceByLine[1000];
long long distanceByColumn[1000];
int main(int argc, char* argv[]){
std::cin.tie(nullptr); std::cout.tie(nullptr);std::ios::sync_with_stdio(false);
int n, m;
std::cin >> n >> m;
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
std::cin >> matrix[i][j];
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
sumByLine[i] += matrix[i][j];
sumByColumn[j] += matrix[i][j];
}
}
prefixByLine[0] = sumByLine[0];
prefixByColumn[0] = sumByColumn[0];
for (int i = 1; i < n; i++) {
prefixByLine[i] = prefixByLine[i-1] + sumByLine[i];
}
for (int i = 1; i < m; i++) {
prefixByColumn[i] = prefixByColumn[i-1] + sumByColumn[i];
}
for (int i = 0; i < n; i++) {
distanceByLine[i] = std::abs(prefixByLine[n-1] - 2*prefixByLine[i]);
}
for (int i = 0; i < m; i++) {
distanceByColumn[i] = std::abs(prefixByColumn[m-1] - 2*prefixByColumn[i]);
}
long long minDistanceByLine = *(std::min_element(distanceByLine, distanceByLine+n));
long long minDistanceByColumn = *(std::min_element(distanceByColumn, distanceByColumn+m));
std::cout << std::min(minDistanceByLine, minDistanceByColumn);
return 0;
}
import java.util.Scanner;
import static java.lang.Math.abs;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int x=sc.nextInt();
int y=sc.nextInt();
long[][] arr = new long[x][y];
long sum=0;
long []line=new long[x];
long []row=new long[y];
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
arr[i][j]=sc.nextInt();
sum+=arr[i][j];
}
}
for (int a = 0; a < x; a++) {
line[a]=0;
for (int b = 0; b < y; b++) {
line[a]+=arr[a][b];
}
}
for (int c = 0; c < y; c++) {
row[c] = 0;
for (int d = 0; d < x; d++) {
row[c] += arr[d][c];
}
}
long lines=line[0];
long rows=row[0];
long linemin=abs(sum-2*line[0]);
long rowmin=abs(sum-2*row[0]);
for (int p = 1; p < x-1; p++) {
lines+=line[p];
if(abs(sum-2*lines)<linemin)linemin=abs(sum-2*lines);
}
for(int q=1;q<y-1;q++){
rows+=row[q];
if(abs(sum-2*rows)<rowmin)rowmin=abs(sum-2*rows);
}
System.out.println(linemin<=rowmin?linemin:rowmin);
}
}
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int m = in.nextInt();
long[][] w = new long[n][m];
// 注意 hasNext 和 hasNextLine 的区别
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
w[i][j] = in.nextLong();
if(i == 0 && j == 0) continue;
if(i == 0) {w[i][j] += w[i][j - 1];continue;}
if(j == 0) {w[i][j] += w[i - 1][j];continue;}
w[i][j] += w[i - 1][j] + w[i][j - 1] - w[i - 1][j - 1];
}
}
long s1 = 0;
long s2 = 0;
long res = Long.MAX_VALUE;
for(int i = 0; i < n ; i++)
{
s1 = w[i][m-1];
s2 = w[n-1][m-1] - s1;
res =Math.min(res,Math.abs(s1-s2));
}
for(int i = 0; i < m ; i++)
{
s1 = w[n-1][i];
s2 = w[n-1][m-1] - s1;
res =Math.min(res,Math.abs(s1-s2));
}
System.out.println(res);
return ;
}
} #include <stdio.h>
long long Min_arr(long long arr[], int n)
{
long long MID = arr[n - 1] / 2;
int left = 0;
int right = n - 2;
int mid = 0;
long long min_arr = 0;
int i=0;
while(arr[i]<MID)
{
i++;
}
if(i==0)
{
min_arr=2*arr[i]-arr[n-1];
}
else{
min_arr=(arr[n-1]-2*arr[i-1]<2*arr[i]-arr[n-1]?arr[n-1]-2*arr[i-1]:2*arr[i]-arr[n-1]);
}
return min_arr;
}
int main() {
int n = 0;
int m = 0;
scanf("%d %d", &n, &m);
int arr[2][22];
long long ROW[2]; //记录每行的叠加和
long long COL[22]; //记录每列的叠加和
int k = 0;
for (k = 0; k < m; k++)
{
COL[k] = 0;
}
for (k = 0; k < n; k++)
{
ROW[k] = 0;
}
int i = 0;
long long row = 0;
for (i = 0; i < n; i++)
{
int j = 0;
for (j = 0; j < m; j++)
{
scanf("%d", &arr[i][j]);
row += arr[i][j];
COL[j] += arr[i][j];
}
ROW[i] += row;
}
long long s = 0;
for (k = 0; k < m; k++)
{
COL[k] += s;
s = COL[k];
}
long long min_ROW = 0;
long long min_COL = 0;
//按行寻找 |s_1-s_2| 的最小值;
min_ROW = Min_arr(ROW, n);
//按列寻找 |s_1-s_2| 的最小值;
min_COL = Min_arr(COL, m);
printf("%lld", min_ROW < min_COL ? min_ROW : min_COL);
return 0;
} 二维数组前缀和
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int h = scanner.nextInt(), w = scanner.nextInt();
int[][] cake = new int[h][w];
long[][] presum = new long[h][w];
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
cake[i][j] = scanner.nextInt();
if (i == 0 && j == 0) {
presum[i][j] = cake[i][j];
} else if (j == 0) {
presum[i][j] = presum[i - 1][0] + cake[i][j];
} else if (i == 0) {
presum[i][j] = presum[i][j - 1] + cake[i][j];
} else {
presum[i][j] = presum[i - 1][j] + presum[i][j - 1] - presum[i - 1][j - 1] + cake[i][j];
}
}
}
long res = Integer.MAX_VALUE;
for (int i = 0; i < h; i++) {
long top = presum[i][w - 1];
long bottom = presum[h - 1][w - 1] - top;
res = Math.min(res, Math.abs(top - bottom));
}
for (int j = 0; j < w; j++) {
long left = presum[h - 1][j];
long right = presum[h - 1][w - 1] - left;
res = Math.min(res, Math.abs(left - right));
}
System.out.println(res);
}
}
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
int n, m,tmp;
long res = INT_MAX;
cin>>n>>m;
vector<vector<long>> sum(n+1,vector<long>(m+1,0));
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
cin>>tmp;
sum[i][j] = tmp+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
}
}
for(int j=1;j<=m;++j) res = min(res,abs(sum[n][m]-2*sum[n][j]));
for(int i=1;i<=n;++i) res = min(res,abs(sum[n][m]-2*sum[i][m]));
cout<<res;
return 0;
} 可以把每行每列的求和算一遍存下来供后续二分时重复使用,用 O(n) 的空间把总求和时间压到 O(nlogn)(其实是 O(n),因为是等比数列求和)
进一步还可以考虑维护以行或列为粒度的前缀和数组,把二分的时间压到 O(logn)
n, m = [int(x) for x in input().strip().split()]
a = [[0] * m for i in range(n)]
row_sum = [0] * n
col_sum = [0] * m
total_sum = 0
for i in range(n):
a[i] = [int(x) for x in input().strip().split()]
row_sum[i] = sum(a[i])
total_sum += row_sum[i]
for j in range(m):
col_sum[j] = sum([a[i][j] for i in range(n)])
half = total_sum >> 1
# row_idx
l = 0; r = n - 1
if l == r:
min_row = total_sum
else:
while (l < r):
mid = l + r + 1 >> 1
half_sum = sum(row_sum[:mid])
if half_sum <= half:
l = mid
else:
r = mid - 1
if half_sum > half:
half_sum -= row_sum[mid-1]
min_row = min(total_sum - 2 * half_sum, 2 * (half_sum + row_sum[r]) - total_sum)
# col_idx
l = 0; r = m - 1
if l == r:
min_col = total_sum
else:
while (l < r):
mid = l + r + 1 >> 1
half_sum = sum(col_sum[:mid])
if half_sum <= half:
l = mid
else:
r = mid - 1
if half_sum > half:
half_sum -= col_sum[mid-1]
min_col = min(total_sum - 2 * half_sum, 2 * (half_sum + col_sum[r]) - total_sum)
print(min(min_row, min_col))
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String[] strings = reader.readLine().split(" ");
int n = Integer.parseInt(strings[0]);
int m = Integer.parseInt(strings[1]);
int[][] cake = new int[n][m];
for (int i = 0; i < n; i++) {
String[] s = reader.readLine().split(" ");
for (int j = 0; j < m; j++) {
cake[i][j] = Integer.parseInt(s[j]);
}
}
long minDiff = Long.MAX_VALUE;
long totalSum = 0;
for (int[] ints : cake) {
for (long anInt : ints) {
totalSum += anInt;
}
}
long sum = 0;
if (cake[0].length == 1) {
System.out.println(totalSum);
}
else {
//先想象把蛋糕横着分开
for (int i = 1; i < n; i++) {
for (int j = 0; j < n - i; j++) {
for (int k = 0; k < m; k++) {
sum += cake[j][k];
}
}
minDiff = Math.min(minDiff, Math.abs(sum - (totalSum - sum)));
sum = 0;
}
//竖着分开的请况
for (int i = 1; i < m; i++) {
for (int[] ints : cake) {
for (int k = 0; k < m - i; k++) {
sum += ints[k];
}
}
minDiff = Math.min(minDiff, Math.abs(sum - (totalSum - sum)));
sum = 0;
}
System.out.println(minDiff);
}
}