package main

import (
	"bufio"
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime/pprof"
	"strconv"
	"time"
	"unicode/utf8"
	"math/rand"
)

// ******************************** CODE STARTS ********************************

const (
)

var (
	n int
	d1 [1 << 12]int64
	d2 [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj + si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1 << 11) + sj - si] = res
}

func solve() {
	n = readInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = readInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n - 1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j + i] + d2[(1 << 11) + j - i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j + i] + d2[(1 << 11) + j - i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	printInt64(best1 + best2)
	printLine()
	printInt(x1 + 1)
	printSpace()
	printInt(y1 + 1)
	printSpace()
	printInt(x2 + 1)
	printSpace()
	printInt(y2 + 1)
	printLine()

}
// ********************************* CODE ENDS *********************************

// IO.

var (
	scanner *Scanner
	writer  *bufio.Writer
	logger  *log.Logger
)

func main() {
	dbg := flag.Bool("dbg", false, "Equals true, if run is in debug mode.")
	opt := flag.Bool("opt", false, "Equals true, if run is in optimized mode.")
	flag.Parse()

	var (
		input  *os.File
		output *os.File
	)

	if *opt || *dbg {
		startTime := time.Now()
		defer func() {
			println(fmt.Sprintf("Time: %.5fs", time.Since(startTime).Seconds()))
		}()
		input, _ = os.Open("/home/outside/coding/workspace/go/io/input.txt")
		output, _ = os.Create("/home/outside/coding/workspace/go/io/output.txt")
		logger = log.New(output, "log: ", 0)
		if *dbg {
			cpuprofile, _ := os.Create("/home/outside/coding/workspace/go/io/cpuprofile.txt")
			pprof.StartCPUProfile(cpuprofile)
			defer cpuprofile.Close()
			defer pprof.StopCPUProfile()
		}
	} else {
		input = os.Stdin
		output = os.Stdout
		// input, _ = os.Open("")
		// output, _ = os.Create("")
	}
	defer input.Close()
	defer output.Close()

	scanner = NewScanner(input)
	scanner.Split(bufio.ScanWords)

	writer = bufio.NewWriter(output)
	defer writer.Flush()

	rand.Seed(int64(time.Now().Nanosecond()))
	solve()
}

// Read.

func readString() string {
	scanner.Scan()
	ans := scanner.Text()
	return ans
}

func readLine() string {
	scanner.Split(bufio.ScanLines)
	defer scanner.Split(bufio.ScanWords)
	return readString()
}

func readInt() int {
	ans, _ := strconv.ParseInt(readString(), 10, 0)
	return int(ans)
}

func readInt64() int64 {
	ans, _ := strconv.ParseInt(readString(), 10, 64)
	return ans
}

func readFloat64() float64 {
	ans, _ := strconv.ParseFloat(readString(), 64)
	return ans
}

// Write.

func printString(value string) {
	writer.WriteString(value)
}

func printLine() {
	printString("\n")
}

func printSpace() {
	printString(" ")
}

func printInt(value int) {
	printString(strconv.FormatInt(int64(value), 10))
}

func printInt64(value int64) {
	printString(strconv.FormatInt(value, 10))
}

func printFloat64(value float64) {
	printString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Log.

func logln(value ...interface{}) {
	writer.Flush()
	logger.Println(value...)
}
func logf(format string, value ...interface{}) {
	writer.Flush()
	logger.Printf(format, value...)
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	return p[j].a < p[i].a
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// Copied scan.go
// Scanner provides a convenient interface for reading data such as
// a file of newline-delimited lines of text. Successive calls to
// the Scan method will step through the 'tokens' of a file, skipping
// the bytes between the tokens. The specification of a token is
// defined by a split function of type SplitFunc; the default split
// function breaks the input into lines with line termination stripped. Split
// functions are defined in this package for scanning a file into
// lines, bytes, UTF-8-encoded runes, and space-delimited words. The
// client may instead provide a custom split function.
//
// Scanning stops unrecoverably at EOF, the first I/O error, or a token too
// large to fit in the buffer. When a scan stops, the reader may have
// advanced arbitrarily far past the last token. Programs that need more
// control over error handling or large tokens, or must run sequential scans
// on a reader, should use bufio.Reader instead.
//
type Scanner struct {
	r            io.Reader // The reader provided by the client.
	split        SplitFunc // The function to split the tokens.
	maxTokenSize int       // Maximum size of a token; modified by tests.
	token        []byte    // Last token returned by split.
	buf          []byte    // Buffer used as argument to split.
	start        int       // First non-processed byte in buf.
	end          int       // End of data in buf.
	err          error     // Sticky error.
}

// SplitFunc is the signature of the split function used to tokenize the
// input. The arguments are an initial substring of the remaining unprocessed
// data and a flag, atEOF, that reports whether the Reader has no more data
// to give. The return values are the number of bytes to advance the input
// and the next token to return to the user, plus an error, if any. If the
// data does not yet hold a complete token, for instance if it has no newline
// while scanning lines, SplitFunc can return (0, nil, nil) to signal the
// Scanner to read more data into the slice and try again with a longer slice
// starting at the same point in the input.
//
// If the returned error is non-nil, scanning stops and the error
// is returned to the client.
//
// The function is never called with an empty data slice unless atEOF
// is true. If atEOF is true, however, data may be non-empty and,
// as always, holds unprocessed text.
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)

// Errors returned by Scanner.
var (
	ErrTooLong         = errors.New("bufio.Scanner: token too long")
	ErrNegativeAdvance = errors.New("bufio.Scanner: SplitFunc returns negative advance count")
	ErrAdvanceTooFar   = errors.New("bufio.Scanner: SplitFunc returns advance count beyond input")
)

const (
	// Maximum size used to buffer a token. The actual maximum token size
	// may be smaller as the buffer may need to include, for instance, a newline.
	MaxScanTokenSize = 1 << 20
)

// NewScanner returns a new Scanner to read from r.
// The split function defaults to ScanLines.
func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        ScanLines,
		maxTokenSize: MaxScanTokenSize,
		buf:          make([]byte, 4096), // Plausible starting size; needn't be large.
	}
}

// Err returns the first non-EOF error that was encountered by the Scanner.
func (s *Scanner) Err() error {
	if s.err == io.EOF {
		return nil
	}
	return s.err
}

// Bytes returns the most recent token generated by a call to Scan.
// The underlying array may point to data that will be overwritten
// by a subsequent call to Scan. It does no allocation.
func (s *Scanner) Bytes() []byte {
	return s.token
}

// Text returns the most recent token generated by a call to Scan
// as a newly allocated string holding its bytes.
func (s *Scanner) Text() string {
	return string(s.token)
}

// Scan advances the Scanner to the next token, which will then be
// available through the Bytes or Text method. It returns false when the
// scan stops, either by reaching the end of the input or an error.
// After Scan returns false, the Err method will return any error that
// occurred during scanning, except that if it was io.EOF, Err
// will return nil.
func (s *Scanner) Scan() bool {
	// Loop until we have a token.
	for {
		// See if we can get a token with what we already have.
		if s.end > s.start {
			advance, token, err := s.split(s.buf[s.start:s.end], s.err != nil)
			if err != nil {
				s.setErr(err)
				return false
			}
			if !s.advance(advance) {
				return false
			}
			s.token = token
			if token != nil {
				return true
			}
		}
		// We cannot generate a token with what we are holding.
		// If we've already hit EOF or an I/O error, we are done.
		if s.err != nil {
			// Shut it down.
			s.start = 0
			s.end = 0
			return false
		}
		// Must read more data.
		// First, shift data to beginning of buffer if there's lots of empty space
		// or space is neded.
		if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
			copy(s.buf, s.buf[s.start:s.end])
			s.end -= s.start
			s.start = 0
		}
		// Is the buffer full? If so, resize.
		if s.end == len(s.buf) {
			if len(s.buf) >= s.maxTokenSize {
				s.setErr(ErrTooLong)
				return false
			}
			newSize := len(s.buf) * 2
			if newSize > s.maxTokenSize {
				newSize = s.maxTokenSize
			}
			newBuf := make([]byte, newSize)
			copy(newBuf, s.buf[s.start:s.end])
			s.buf = newBuf
			s.end -= s.start
			s.start = 0
			continue
		}
		// Finally we can read some input. Make sure we don't get stuck with
		// a misbehaving Reader. Officially we don't need to do this, but let's
		// be extra careful: Scanner is for safe, simple jobs.
		for loop := 0; ; {
			n, err := s.r.Read(s.buf[s.end:len(s.buf)])
			s.end += n
			if err != nil {
				s.setErr(err)
				break
			}
			if n > 0 {
				break
			}
			loop++
			if loop > 100 {
				s.setErr(io.ErrNoProgress)
				break
			}
		}
	}
}

// advance consumes n bytes of the buffer. It reports whether the advance was legal.
func (s *Scanner) advance(n int) bool {
	if n < 0 {
		s.setErr(ErrNegativeAdvance)
		return false
	}
	if n > s.end-s.start {
		s.setErr(ErrAdvanceTooFar)
		return false
	}
	s.start += n
	return true
}

// setErr records the first error encountered.
func (s *Scanner) setErr(err error) {
	if s.err == nil || s.err == io.EOF {
		s.err = err
	}
}

// Split sets the split function for the Scanner. If called, it must be
// called before Scan. The default split function is ScanLines.
func (s *Scanner) Split(split SplitFunc) {
	s.split = split
}

// Split functions

// ScanBytes is a split function for a Scanner that returns each byte as a token.
func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	return 1, data[0:1], nil
}

var errorRune = []byte(string(utf8.RuneError))

// ScanRunes is a split function for a Scanner that returns each
// UTF-8-encoded rune as a token. The sequence of runes returned is
// equivalent to that from a range loop over the input as a string, which
// means that erroneous UTF-8 encodings translate to U+FFFD = "\xef\xbf\xbd".
// Because of the Scan interface, this makes it impossible for the client to
// distinguish correctly encoded replacement runes from encoding errors.
func ScanRunes(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	// Fast path 1: ASCII.
	if data[0] < utf8.RuneSelf {
		return 1, data[0:1], nil
	}

	// Fast path 2: Correct UTF-8 decode without error.
	_, width := utf8.DecodeRune(data)
	if width > 1 {
		// It's a valid encoding. Width cannot be one for a correctly encoded
		// non-ASCII rune.
		return width, data[0:width], nil
	}

	// We know it's an error: we have width==1 and implicitly r==utf8.RuneError.
	// Is the error because there wasn't a full rune to be decoded?
	// FullRune distinguishes correctly between erroneous and incomplete encodings.
	if !atEOF && !utf8.FullRune(data) {
		// Incomplete; get more bytes.
		return 0, nil, nil
	}

	// We have a real UTF-8 encoding error. Return a properly encoded error rune
	// but advance only one byte. This matches the behavior of a range loop over
	// an incorrectly encoded string.
	return 1, errorRune, nil
}

// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)-1] == '\r' {
		return data[0 : len(data)-1]
	}
	return data
}

// ScanLines is a split function for a Scanner that returns each line of
// text, stripped of any trailing end-of-line marker. The returned line may
// be empty. The end-of-line marker is one optional carriage return followed
// by one mandatory newline. In regular expression notation, it is `\r?\n`.
// The last non-empty line of input will be returned even if it has no
// newline.
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	if i := bytes.IndexByte(data, '\n'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, dropCR(data[0:i]), nil
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), dropCR(data), nil
	}
	// Request more data.
	return 0, nil, nil
}

// isSpace reports whether the character is a Unicode white space character.
// We avoid dependency on the unicode package, but check validity of the implementation
// in the tests.
func isSpace(r rune) bool {
	if r <= '\u00FF' {
		// Obvious ASCII ones: \t through \r plus space. Plus two Latin-1 oddballs.
		switch r {
		case ' ', '\t', '\n', '\v', '\f', '\r':
			return true
		case '\u0085', '\u00A0':
			return true
		}
		return false
	}
	// High-valued ones.
	if '\u2000' <= r && r <= '\u200a' {
		return true
	}
	switch r {
	case '\u1680', '\u180e', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000':
		return true
	}
	return false
}

// ScanWords is a split function for a Scanner that returns each
// space-separated word of text, with surrounding spaces deleted. It will
// never return an empty string. The definition of space is set by
// unicode.IsSpace.
func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error) {
	// Skip leading spaces.
	start := 0
	for width := 0; start < len(data); start += width {
		var r rune
		r, width = utf8.DecodeRune(data[start:])
		if !isSpace(r) {
			break
		}
	}
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	// Scan until space, marking end of word.
	for width, i := 0, start; i < len(data); i += width {
		var r rune
		r, width = utf8.DecodeRune(data[i:])
		if isSpace(r) {
			return i + width, data[start:i], nil
		}
	}
	// If we're at EOFadsf, we have a final, non-empty, non-terminated word. Return it.
	if atEOF && len(data) > start {
		return len(data), data[start:], nil
	}
	// Request more data.
	return 0, nil, nil
}

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

var (
	in  = bufio.NewScanner(os.Stdin)
	out = bufio.NewWriter(os.Stdout)
)

type point struct {
	x int
	y int
	s int64
}

var (
	a   [2500][2500]int
	d1  [5000]int64
	d2  [5000]int64
	n   int
	tmp point
	ans [2]point
)

func main() {
	defer out.Flush()
	in.Split(bufio.ScanWords)

	read(&n)

	for i := 1; i <= n; i++ {
		for j := 1; j <= n; j++ {
			read(&a[i][j])
		}
	}

	for i := 1; i <= n; i++ {
		for j := 1; j <= n; j++ {
			d1[i+j] += int64(a[i][j])
			d2[n-i+j] += int64(a[i][j])
		}
	}

	for i := 1; i <= n; i++ {
		for j := 1; j <= n; j++ {

			tmp = point{i, j, d1[i+j] + d2[n-i+j] - int64(a[i][j])}

			ans[(i+j)%2] = max(ans[(i+j)%2], tmp)

		}
	}

	fmt.Println(ans[0].s + ans[1].s)
	fmt.Print(ans[0].x, " ", ans[0].y, " ", ans[1].x, " ", ans[1].y)

}

func max(a point, b point) point {
	if a.s > b.s {
		return a
	} else {
		return b
	}
}

func read(num *int) {
	in.Scan()
	*num, _ = strconv.Atoi(in.Text())
}

func writeln(str string) {
	out.WriteString(str + "\n")
}

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"runtime/debug"
	"strconv"
	"time"
)

const (
	maxStackSize int = 256 * (1 << 20)
)

func main() {
	debug.SetMaxStack(maxStackSize)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

const (
	minWordBufferSize int = 32
	minLineBufferSize int = 32
)

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	word := make([]byte, minWordBufferSize)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if character == ' ' || character == '\n' || character == '\r' || character == '\t' {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				newWord := make([]byte, len(word)*2)
				copy(newWord, word)
				word = newWord
			}
			word[i] = character
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	line := make([]byte, minLineBufferSize)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if character == '\r' {
			continue
		}
		if character == '\n' {
			if i != 0 {
				break
			}
		} else {
			if i == len(line) {
				newLine := make([]byte, len(line)*2)
				copy(newLine, line)
				line = newLine
			}
			line[i] = character
			i++
		}
	}
	return string(line[:i])
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_STACK_SIZE  int = 256 * (1 << 20)
	MIN_BUFFER_SIZE int = 1 << 20
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	 word := make([]byte, 64)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				newWord := make([]byte, len(word)*2)
				copy(newWord, word)
				word = newWord
			}
			word[i] = character
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	var line []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) &&
			character != '\n' && character != '\t' && character != ' ' {
			continue
		}
		if character == '\n' {
			break
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_STACK_SIZE  int = 256 * (1 << 20)
	MIN_BUFFER_SIZE int = 1 << 20
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	word := make([]byte, 64)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if character == ' ' || character == '\n' || character == '\r' || character == '\t' {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				word = append(word, character)
			} else {
				word[i] = character
			}
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	var line []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) &&
			character != '\n' && character != '\t' && character != ' ' {
			continue
		}
		if character == '\n' {
			break
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_STACK_SIZE  int = 256 * (1 << 20)
	MIN_BUFFER_SIZE int = 1 << 20
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	 word := make([]byte, 20)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				newWord := make([]byte, len(word)*2)
				copy(newWord, word)
				word = newWord
			}
			word[i] = character
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	var line []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) &&
			character != '\n' && character != '\t' && character != ' ' {
			continue
		}
		if character == '\n' {
			break
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"
)

const (
	MAX_STACK_SIZE  int = 256 * (1 << 20)
	MIN_BUFFER_SIZE int = 1 << 20
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	word := make([]byte, 2)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if character == ' ' || character == '\n' || character == '\r' || character == '\t' {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				newWord := make([]byte, len(word)*2)
				copy(newWord, word)
				word = newWord
			}
			word[i] = character
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	line := make([]byte, 64)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if character == '\r' {
			continue
		}
		if character == '\n' {
			if i != 0 {
				break
			}
		} else {
			if i == len(line) {
				line = append(line, character)
			} else {
				line[i] = character
			}
			i++
		}
	}
	return string(line[:i])
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_STACK_SIZE  int = 256 * (1 << 20)
	MIN_BUFFER_SIZE int = 1 << 20
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	word := make([]byte, 64)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				word = append(word, character)
			} else {
				word[i] = character
			}
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	var line []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) &&
			character != '\n' && character != '\t' && character != ' ' {
			continue
		}
		if character == '\n' {
			break
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_STACK_SIZE  int = 256 * (1 << 20)
	MIN_BUFFER_SIZE int = 1 << 20
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReaderSize(r, MIN_BUFFER_SIZE)}
}

func (r *Reader) ReadWord() string {
	var word []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (r *Reader) ReadLine() string {
	var line []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) &&
			character != '\n' && character != '\t' && character != ' ' {
			continue
		}
		if character == '\n' {
			break
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriterSize(w, MIN_BUFFER_SIZE)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_STACK_SIZE  int = 256 * (1 << 20)
	MIN_BUFFER_SIZE int = 1 << 20
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	word := make([]byte, 32)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				newWord := make([]byte, len(word)*2)
				copy(newWord, word)
				word = newWord
			}
			word[i] = character
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	var line []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) &&
			character != '\n' && character != '\t' && character != ' ' {
			continue
		}
		if character == '\n' {
			break
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_STACK_SIZE  int = 256 * (1 << 20)
	MIN_BUFFER_SIZE int = 1 << 20
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReaderSize(r, MIN_BUFFER_SIZE)}
}

func (r *Reader) ReadWord() string {
	var word []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if character == ' ' || character == '\n' || character == '\r' {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (r *Reader) ReadLine() string {
	var line []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) &&
			character != '\n' && character != '\t' && character != ' ' {
			continue
		}
		if character == '\n' {
			break
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriterSize(w, MIN_BUFFER_SIZE)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_STACK_SIZE  int = 256 * (1 << 20)
	MIN_BUFFER_SIZE int = 1 << 20
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	 word := make([]byte, 64)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				word = append(word, word...)
			}
			word[i] = character
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	var line []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) &&
			character != '\n' && character != '\t' && character != ' ' {
			continue
		}
		if character == '\n' {
			break
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_STACK_SIZE  int = 256 * (1 << 20)
	MIN_BUFFER_SIZE int = 1 << 20
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	var word []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (r *Reader) ReadLine() string {
	var line []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) &&
			character != '\n' && character != '\t' && character != ' ' {
			continue
		}
		if character == '\n' {
			break
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"runtime/debug"
	"strconv"
	"time"
)

const (
	maxStackSize int = 256 * (1 << 20)
)

func main() {
	debug.SetMaxStack(maxStackSize)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

const (
	minWordBufferSize int = 32
	minLineBufferSize int = 32
)

func isSpace(c byte) bool {
	return c <= 32
}

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	word := make([]byte, minWordBufferSize)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if character <= 32 {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				newWord := make([]byte, len(word)*2)
				copy(newWord, word)
				word = newWord
			}
			word[i] = character
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	line := make([]byte, minLineBufferSize)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if character == '\n' {
			if i != 0 {
				break
			}
		} else {
			if i == len(line) {
				newLine := make([]byte, len(line)*2)
				copy(newLine, line)
				line = newLine
			}
			line[i] = character
			i++
		}
	}
	return string(line[:i])
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime/pprof"
	"strconv"
	"time"
	"unicode/utf8"
	"math/rand"
)

// ******************************** CODE STARTS ********************************

const (
)

var (
	n int
	d1 [1 << 12]int64
	d2 [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj + si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1 << 11) + sj - si] = res
}

func solve() {
	n = readInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = readInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n - 1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j + i] + d2[(1 << 11) + j - i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j + i] + d2[(1 << 11) + j - i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	printInt64(best1 + best2)
	printLine()
	printInt(x1 + 1)
	printSpace()
	printInt(y1 + 1)
	printSpace()
	printInt(x2 + 1)
	printSpace()
	printInt(y2 + 1)
	printLine()

}
// ********************************* CODE ENDS *********************************

// IO.

var (
	scanner *Scanner
	writer  *bufio.Writer
	logger  *log.Logger
)

func main() {
	dbg := flag.Bool("dbg", false, "Equals true, if run is in debug mode.")
	opt := flag.Bool("opt", false, "Equals true, if run is in optimized mode.")
	flag.Parse()

	var (
		input  *os.File
		output *os.File
	)

	if *opt || *dbg {
		startTime := time.Now()
		defer func() {
			println(fmt.Sprintf("Time: %.5fs", time.Since(startTime).Seconds()))
		}()
		input, _ = os.Open("/home/outside/coding/workspace/go/io/input.txt")
		output, _ = os.Create("/home/outside/coding/workspace/go/io/output.txt")
		logger = log.New(output, "log: ", 0)
		if *dbg {
			cpuprofile, _ := os.Create("/home/outside/coding/workspace/go/io/cpuprofile.txt")
			pprof.StartCPUProfile(cpuprofile)
			defer cpuprofile.Close()
			defer pprof.StopCPUProfile()
		}
	} else {
		input = os.Stdin
		output = os.Stdout
		// input, _ = os.Open("")
		// output, _ = os.Create("")
	}
	defer input.Close()
	defer output.Close()

	scanner = NewScanner(input)
	scanner.Split(bufio.ScanWords)

	writer = bufio.NewWriter(output)
	defer writer.Flush()

	rand.Seed(int64(time.Now().Nanosecond()))
	solve()
}

// Read.

func readString() string {
	scanner.Scan()
	ans := scanner.Text()
	return ans
}

func readLine() string {
	scanner.Split(bufio.ScanLines)
	defer scanner.Split(bufio.ScanWords)
	return readString()
}

func readInt() int {
	ans, _ := strconv.ParseInt(readString(), 10, 0)
	return int(ans)
}

func readInt64() int64 {
	ans, _ := strconv.ParseInt(readString(), 10, 64)
	return ans
}

func readFloat64() float64 {
	ans, _ := strconv.ParseFloat(readString(), 64)
	return ans
}

// Write.

func printString(value string) {
	writer.WriteString(value)
}

func printLine() {
	printString("\n")
}

func printSpace() {
	printString(" ")
}

func printInt(value int) {
	printString(strconv.FormatInt(int64(value), 10))
}

func printInt64(value int64) {
	printString(strconv.FormatInt(value, 10))
}

func printFloat64(value float64) {
	printString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Log.

func logln(value ...interface{}) {
	writer.Flush()
	logger.Println(value...)
}
func logf(format string, value ...interface{}) {
	writer.Flush()
	logger.Printf(format, value...)
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	return p[j].a < p[i].a
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// Copied scan.go
// Scanner provides a convenient interface for reading data such as
// a file of newline-delimited lines of text. Successive calls to
// the Scan method will step through the 'tokens' of a file, skipping
// the bytes between the tokens. The specification of a token is
// defined by a split function of type SplitFunc; the default split
// function breaks the input into lines with line termination stripped. Split
// functions are defined in this package for scanning a file into
// lines, bytes, UTF-8-encoded runes, and space-delimited words. The
// client may instead provide a custom split function.
//
// Scanning stops unrecoverably at EOF, the first I/O error, or a token too
// large to fit in the buffer. When a scan stops, the reader may have
// advanced arbitrarily far past the last token. Programs that need more
// control over error handling or large tokens, or must run sequential scans
// on a reader, should use bufio.Reader instead.
//
type Scanner struct {
	r            io.Reader // The reader provided by the client.
	split        SplitFunc // The function to split the tokens.
	maxTokenSize int       // Maximum size of a token; modified by tests.
	token        []byte    // Last token returned by split.
	buf          []byte    // Buffer used as argument to split.
	start        int       // First non-processed byte in buf.
	end          int       // End of data in buf.
	err          error     // Sticky error.
}

// SplitFunc is the signature of the split function used to tokenize the
// input. The arguments are an initial substring of the remaining unprocessed
// data and a flag, atEOF, that reports whether the Reader has no more data
// to give. The return values are the number of bytes to advance the input
// and the next token to return to the user, plus an error, if any. If the
// data does not yet hold a complete token, for instance if it has no newline
// while scanning lines, SplitFunc can return (0, nil, nil) to signal the
// Scanner to read more data into the slice and try again with a longer slice
// starting at the same point in the input.
//
// If the returned error is non-nil, scanning stops and the error
// is returned to the client.
//
// The function is never called with an empty data slice unless atEOF
// is true. If atEOF is true, however, data may be non-empty and,
// as always, holds unprocessed text.
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)

// Errors returned by Scanner.
var (
	ErrTooLong         = errors.New("bufio.Scanner: token too long")
	ErrNegativeAdvance = errors.New("bufio.Scanner: SplitFunc returns negative advance count")
	ErrAdvanceTooFar   = errors.New("bufio.Scanner: SplitFunc returns advance count beyond input")
)

const (
	// Maximum size used to buffer a token. The actual maximum token size
	// may be smaller as the buffer may need to include, for instance, a newline.
	MaxScanTokenSize = 1 << 20
)

// NewScanner returns a new Scanner to read from r.
// The split function defaults to ScanLines.
func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		r:            r,
		split:        ScanLines,
		maxTokenSize: MaxScanTokenSize,
		buf:          make([]byte, 4096), // Plausible starting size; needn't be large.
	}
}

// Err returns the first non-EOF error that was encountered by the Scanner.
func (s *Scanner) Err() error {
	if s.err == io.EOF {
		return nil
	}
	return s.err
}

// Bytes returns the most recent token generated by a call to Scan.
// The underlying array may point to data that will be overwritten
// by a subsequent call to Scan. It does no allocation.
func (s *Scanner) Bytes() []byte {
	return s.token
}

// Text returns the most recent token generated by a call to Scan
// as a newly allocated string holding its bytes.
func (s *Scanner) Text() string {
	return string(s.token)
}

// Scan advances the Scanner to the next token, which will then be
// available through the Bytes or Text method. It returns false when the
// scan stops, either by reaching the end of the input or an error.
// After Scan returns false, the Err method will return any error that
// occurred during scanning, except that if it was io.EOF, Err
// will return nil.
func (s *Scanner) Scan() bool {
	// Loop until we have a token.
	for {
		// See if we can get a token with what we already have.
		if s.end > s.start {
			advance, token, err := s.split(s.buf[s.start:s.end], s.err != nil)
			if err != nil {
				s.setErr(err)
				return false
			}
			if !s.advance(advance) {
				return false
			}
			s.token = token
			if token != nil {
				return true
			}
		}
		// We cannot generate a token with what we are holding.
		// If we've already hit EOF or an I/O error, we are done.
		if s.err != nil {
			// Shut it down.
			s.start = 0
			s.end = 0
			return false
		}
		// Must read more data.
		// First, shift data to beginning of buffer if there's lots of empty space
		// or space is neded.
		if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
			copy(s.buf, s.buf[s.start:s.end])
			s.end -= s.start
			s.start = 0
		}
		// Is the buffer full? If so, resize.
		if s.end == len(s.buf) {
			if len(s.buf) >= s.maxTokenSize {
				s.setErr(ErrTooLong)
				return false
			}
			newSize := len(s.buf) * 2
			if newSize > s.maxTokenSize {
				newSize = s.maxTokenSize
			}
			newBuf := make([]byte, newSize)
			copy(newBuf, s.buf[s.start:s.end])
			s.buf = newBuf
			s.end -= s.start
			s.start = 0
			continue
		}
		// Finally we can read some input. Make sure we don't get stuck with
		// a misbehaving Reader. Officially we don't need to do this, but let's
		// be extra careful: Scanner is for safe, simple jobs.
		for loop := 0; ; {
			n, err := s.r.Read(s.buf[s.end:len(s.buf)])
			s.end += n
			if err != nil {
				s.setErr(err)
				break
			}
			if n > 0 {
				break
			}
			loop++
			if loop > 100 {
				s.setErr(io.ErrNoProgress)
				break
			}
		}
	}
}

// advance consumes n bytes of the buffer. It reports whether the advance was legal.
func (s *Scanner) advance(n int) bool {
	if n < 0 {
		s.setErr(ErrNegativeAdvance)
		return false
	}
	if n > s.end-s.start {
		s.setErr(ErrAdvanceTooFar)
		return false
	}
	s.start += n
	return true
}

// setErr records the first error encountered.
func (s *Scanner) setErr(err error) {
	if s.err == nil || s.err == io.EOF {
		s.err = err
	}
}

// Split sets the split function for the Scanner. If called, it must be
// called before Scan. The default split function is ScanLines.
func (s *Scanner) Split(split SplitFunc) {
	s.split = split
}

// Split functions

// ScanBytes is a split function for a Scanner that returns each byte as a token.
func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	return 1, data[0:1], nil
}

var errorRune = []byte(string(utf8.RuneError))

// ScanRunes is a split function for a Scanner that returns each
// UTF-8-encoded rune as a token. The sequence of runes returned is
// equivalent to that from a range loop over the input as a string, which
// means that erroneous UTF-8 encodings translate to U+FFFD = "\xef\xbf\xbd".
// Because of the Scan interface, this makes it impossible for the client to
// distinguish correctly encoded replacement runes from encoding errors.
func ScanRunes(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	// Fast path 1: ASCII.
	if data[0] < utf8.RuneSelf {
		return 1, data[0:1], nil
	}

	// Fast path 2: Correct UTF-8 decode without error.
	_, width := utf8.DecodeRune(data)
	if width > 1 {
		// It's a valid encoding. Width cannot be one for a correctly encoded
		// non-ASCII rune.
		return width, data[0:width], nil
	}

	// We know it's an error: we have width==1 and implicitly r==utf8.RuneError.
	// Is the error because there wasn't a full rune to be decoded?
	// FullRune distinguishes correctly between erroneous and incomplete encodings.
	if !atEOF && !utf8.FullRune(data) {
		// Incomplete; get more bytes.
		return 0, nil, nil
	}

	// We have a real UTF-8 encoding error. Return a properly encoded error rune
	// but advance only one byte. This matches the behavior of a range loop over
	// an incorrectly encoded string.
	return 1, errorRune, nil
}

// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)-1] == '\r' {
		return data[0 : len(data)-1]
	}
	return data
}

// ScanLines is a split function for a Scanner that returns each line of
// text, stripped of any trailing end-of-line marker. The returned line may
// be empty. The end-of-line marker is one optional carriage return followed
// by one mandatory newline. In regular expression notation, it is `\r?\n`.
// The last non-empty line of input will be returned even if it has no
// newline.
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	if i := bytes.IndexByte(data, '\n'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, dropCR(data[0:i]), nil
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), dropCR(data), nil
	}
	// Request more data.
	return 0, nil, nil
}

// isSpace reports whether the character is a Unicode white space character.
// We avoid dependency on the unicode package, but check validity of the implementation
// in the tests.
func isSpace(r rune) bool {
	if r <= '\u00FF' {
		// Obvious ASCII ones: \t through \r plus space. Plus two Latin-1 oddballs.
		switch r {
		case ' ', '\t', '\n', '\v', '\f', '\r':
			return true
		case '\u0085', '\u00A0':
			return true
		}
		return false
	}
	// High-valued ones.
	if '\u2000' <= r && r <= '\u200a' {
		return true
	}
	switch r {
	case '\u1680', '\u180e', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000':
		return true
	}
	return false
}

// ScanWords is a split function for a Scanner that returns each
// space-separated word of text, with surrounding spaces deleted. It will
// never return an empty string. The definition of space is set by
// unicode.IsSpace.
func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error) {
	// Skip leading spaces.
	start := 0
	for width := 0; start < len(data); start += width {
		var r rune
		r, width = utf8.DecodeRune(data[start:])
		if !isSpace(r) {
			break
		}
	}
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	// Scan until space, marking end of word.
	for width, i := 0, start; i < len(data); i += width {
		var r rune
		r, width = utf8.DecodeRune(data[i:])
		if isSpace(r) {
			return i + width, data[start:i], nil
		}
	}
	// If we're at EOF, we have a final, non-empty, non-terminated word. Return it.
	if atEOF && len(data) > start {
		return len(data), data[start:], nil
	}
	// Request more data.
	return 0, nil, nil
}

package main

import (
	"bytes"
	"errors"
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_INPUT_SIZE int = 1 << 26
	MAX_STACK_SIZE int = 256 * (1 << 20)
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")

	input := make([]byte, MAX_INPUT_SIZE)
	fileLength, _ := inputFile.Read(input)
	input = input[:fileLength]

	inputFile.Close()

	in := NewExtendedBuffer(input)
	out := NewExtendedBuffer([]byte{})

	solve(in, out)

	output := out.Bytes()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")

	outputFile.Write(output)

	outputFile.Sync()
	outputFile.Close()
}

// IO.

type ExtendedBuffer struct {
	*bytes.Buffer
}

func NewExtendedBuffer(buf []byte) *ExtendedBuffer {
	// Cleans up space characters.
	for i := 0; i < len(buf); i++ {
		if buf[i] != '\n' && unicode.IsSpace(rune(buf[i])) {
			buf[i] = ' '
		}
	}
	return &ExtendedBuffer{bytes.NewBuffer(buf)}
}

// Read.

func (in *ExtendedBuffer) ReadWord() string {
	var word []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}
		if character == ' ' || character == '\n' {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (in *ExtendedBuffer) ReadLine() string {
	var line []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}
		if character == '\n' {
			if len(line) != 0 {
				break
			}
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (in *ExtendedBuffer) ReadInt() int {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 0)
	return int(ans)
}

func (in *ExtendedBuffer) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 64)
	return ans
}

func (in *ExtendedBuffer) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(in.ReadWord(), 64)
	return ans
}

// Print.

func (out *ExtendedBuffer) PrintString(value string) {
	out.Write([]byte(value))
}

func (out *ExtendedBuffer) PrintLine() {
	out.PrintString("\n")
}

func (out *ExtendedBuffer) PrintSpace() {
	out.PrintString(" ")
}

func (out *ExtendedBuffer) PrintInt(value int) {
	out.PrintString(strconv.FormatInt(int64(value), 10))
}

func (out *ExtendedBuffer) PrintUint(value uint) {
	out.PrintString(strconv.FormatUint(uint64(value), 10))
}

func (out *ExtendedBuffer) PrintInt64(value int64) {
	out.PrintString(strconv.FormatInt(value, 10))
}

func (out *ExtendedBuffer) PrintUint64(value uint64) {
	out.PrintString(strconv.FormatUint(value, 10))
}

func (out *ExtendedBuffer) PrintFloat64(value float64) {
	out.PrintString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(in *ExtendedBuffer, out *ExtendedBuffer) {
	n = in.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = in.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	// out.PrintInt64(best1 + best2)
	// out.PrintLine()
	// out.PrintInt(x1 + 1)
	// out.PrintSpace()
	// out.PrintInt(y1 + 1)
	// out.PrintSpace()
	// out.PrintInt(x2 + 1)
	// out.PrintSpace()
	// out.PrintInt(y2 + 1)
	// out.PrintLine()
	fmt.Println(best1 + best2)
	fmt.Print(x1 + 1)
	fmt.Print(" ")
	fmt.Print(y1 + 1)
	fmt.Print(" ")
	fmt.Print(x2 + 1)
	fmt.Print(" ")
	fmt.Println(y2 + 1)
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_STACK_SIZE  int = 256 * (1 << 20)
	MIN_BUFFER_SIZE int = 1 << 20
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	 word := make([]byte, 64)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				word = append(word, ' ')
			}
			word[i] = character
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	var line []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) &&
			character != '\n' && character != '\t' && character != ' ' {
			continue
		}
		if character == '\n' {
			break
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"
)

const (
	MAX_STACK_SIZE  int = 256 * (1 << 20)
	MIN_BUFFER_SIZE int = 1 << 20
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	word := make([]byte, 2)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if character == ' ' || character == '\n' || character == '\r' || character == '\t' {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				word = append(word, character)
			} else {
				word[i] = character
			}
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	line := make([]byte, 64)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if character == '\r' {
			continue
		}
		if character == '\n' {
			if i != 0 {
				break
			}
		} else {
			if i == len(line) {
				line = append(line, character)
			} else {
				line[i] = character
			}
			i++
		}
	}
	return string(line[:i])
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"runtime/debug"
	"strconv"
	"time"
)

const (
	maxStackSize int = 256 * (1 << 20)
)

func main() {
	debug.SetMaxStack(maxStackSize)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

const (
	minWordBufferSize int = 32
	minLineBufferSize int = 32
)

func isSpace(c byte) bool {
	return c <= 32
}

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	word := make([]byte, minWordBufferSize)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if isSpace(character) {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				newWord := make([]byte, len(word)*2)
				copy(newWord, word)
				word = newWord
			}
			word[i] = character
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	line := make([]byte, minLineBufferSize)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if character == '\n' {
			if i != 0 {
				break
			}
		} else {
			if i == len(line) {
				newLine := make([]byte, len(line)*2)
				copy(newLine, line)
				line = newLine
			}
			line[i] = character
			i++
		}
	}
	return string(line[:i])
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"runtime/debug"
	"strconv"
	"time"
)

const (
	maxStackSize int = 256 * (1 << 20)
)

func main() {
	debug.SetMaxStack(maxStackSize)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

const (
	minWordBufferSize int = 32
	minLineBufferSize int = 32
)

func isSpace(c byte) bool {
	return c <= 32
}

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	word := make([]byte, minWordBufferSize)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if isSpace(character) {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				newWord := make([]byte, len(word)*2)
				copy(newWord, word)
				word = newWord
			}
			word[i] = character
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	line := make([]byte, minLineBufferSize)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if character == '\n' {
			if i != 0 {
				break
			}
		} else {
			if i == len(line) {
				newLine := make([]byte, len(line)*2)
				copy(newLine, line)
				line = newLine
			}
			line[i] = character
			i++
		}
	}
	return string(line[:i])
}

func (r *Reader) ReadInt() int {
	character, err := r.ReadByte()
	for err == nil && isSpace(character) {
		character, err = r.ReadByte()
	}
	if err != nil {
		panic("Can't read input.")
	}

	sign := 1
	if character == '-' {
		sign = -1
		character, err = r.ReadByte()
	}

	result := 0
	for err == nil && !isSpace(character) {
		result = result*10 + int(character-'0')
		character, err = r.ReadByte()
	}

	return sign * result
	// ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	// return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	// Log(n)
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
			// Log(array[j][i])
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"fmt"
	"os"
)

type Value struct {
	x, y, val int64
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)
	defer writer.Flush()

	var n int
	_, _ = fmt.Fscanf(reader, "%d\n", &n)

	matrix := make([][]int64, n)
	mapLR := [4010]int64{}
	mapRL := [4010]int64{}

	for i := 0; i < n; i++ {
		matrix[i] = make([]int64, n)
		for j := 0; j < n; j++ {
			_, _ = fmt.Fscanf(reader, "%d", &matrix[i][j])
			mapRL[i+j] += matrix[i][j]
			mapLR[i-j+n] += matrix[i][j]
		}
		_, _ = fmt.Fscanf(reader, "\n")
	}

	bc := make([]Value, 2,)
	bc[0].val = -1
	bc[1].val = -1

	for i:= 0; i < n; i++ {
		for j := 0; j < n; j++ {
			curVal := mapRL[i+j] + mapLR[i-j+n] - matrix[i][j]
			e:= (i+j)&1
			if bc[e].val < curVal {
				bc[e] =  Value{int64(i),int64(j),curVal}
			}
		}
	}
	_, _ = fmt.Fprintf(writer, "%d\n", bc[0].val+bc[1].val)
	_, _ = fmt.Fprintf(writer, "%d %d %d %d\n", bc[0].x+1, bc[0].y+1, bc[1].x+1, bc[1].y+1)
}

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_STACK_SIZE  int = 256 * (1 << 20)
	MIN_BUFFER_SIZE int = 1 << 20
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	 word := make([]byte, 1)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				newWord := make([]byte, len(word)*2)
				copy(newWord, word)
				word = newWord
			}
			word[i] = character
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	var line []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) &&
			character != '\n' && character != '\t' && character != ' ' {
			continue
		}
		if character == '\n' {
			break
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

// Author: sighduck
// URL: https://codeforces.com/problemset/problem/463/C

package main

import (
	"bufio"
	"fmt"
	"math"
	"os"
)

type Bishop struct {
	X int
	Y int
}

type Solution struct {
	Cost int64
	B1   Bishop
	B2   Bishop
}

func Solve(n int, board [][]int64) Solution {
	d1, d2 := make([]int64, 2*n+1), make([]int64, 2*n+1)
	for i := 0; i < n; i++ {
		for j := 0; j < n; j++ {
			d1[i+j] += board[i][j]
			d2[i-j+n] += board[i][j]
		}
	}
	maxEvenDiagonal, maxOddDiagonal := int64(math.MinInt64), int64(math.MinInt64)
	maxEvenBishop, maxOddBishop := Bishop{}, Bishop{}
	for i := 0; i < n; i++ {
		for j := 0; j < n; j++ {
			diagonalSum := d1[i+j] + d2[i-j+n] - board[i][j]
			if (i+j)%2 == 0 && diagonalSum > maxEvenDiagonal {
				maxEvenDiagonal = diagonalSum
				maxEvenBishop = Bishop{i + 1, j + 1}
			} else if (i+j)%2 == 1 && diagonalSum > maxOddDiagonal {
				maxOddDiagonal = diagonalSum
				maxOddBishop = Bishop{i + 1, j + 1}
			}
		}
	}
	return Solution{Cost: maxEvenDiagonal + maxOddDiagonal, B1: maxEvenBishop, B2: maxOddBishop}
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)
	defer writer.Flush()

	var n int
	fmt.Fscanf(reader, "%d\n", &n)
	board := make([][]int64, n)
	for i := 0; i < n; i++ {
		board[i] = make([]int64, n)
		for j := 0; j < n; j++ {
			fmt.Fscanf(reader, "%d", &board[i][j])
		}
		fmt.Fscanf(reader, "\n")
	}

	solution := Solve(n, board)
	fmt.Fprintf(writer, "%d\n", solution.Cost)
	fmt.Fprintf(writer, "%d %d %d %d\n", solution.B1.X, solution.B1.Y, solution.B2.X, solution.B2.Y)
}

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"runtime/debug"
	"strconv"
	"time"
)

const (
	maxStackSize int = 256 * (1 << 20)
)

func main() {
	debug.SetMaxStack(maxStackSize)

	// Disables garbage collector.
	// debug.SetGCPercent(-1)

	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

const (
	minWordBufferSize int = 32
	minLineBufferSize int = 32
)

func isSpace(c byte) bool {
	return c < 33
}

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReader(r)}
}

func (r *Reader) ReadWord() string {
	word := make([]byte, minWordBufferSize)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if isSpace(character) {
			if i != 0 {
				break
			}
		} else {
			if i == len(word) {
				newWord := make([]byte, len(word)*2)
				copy(newWord, word)
				word = newWord
			}
			word[i] = character
			i++
		}
	}
	return string(word[:i])
}

func (r *Reader) ReadLine() string {
	line := make([]byte, minLineBufferSize)
	i := 0
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if character == '\n' {
			if i != 0 {
				break
			}
		} else {
			if i == len(line) {
				newLine := make([]byte, len(line)*2)
				copy(newLine, line)
				line = newLine
			}
			line[i] = character
			i++
		}
	}
	return string(line[:i])
}

func (r *Reader) ReadInt() int {
	character, err := r.ReadByte()
	for err == nil && isSpace(character) {
		character, err = r.ReadByte()
	}
	if err != nil {
		panic("Can't read input.")
	}

	sign := 1
	if character == '-' {
		sign = -1
		character, err = r.ReadByte()
	}

	result := 0
	for err == nil && !isSpace(character) {
		result = result*10 + int(character-'0')
		character, err = r.ReadByte()
	}

	return sign * result
}

func (r *Reader) ReadInt64() int64 {
	character, err := r.ReadByte()
	for err == nil && isSpace(character) {
		character, err = r.ReadByte()
	}
	if err != nil {
		panic("Can't read input.")
	}

	sign := int64(1)
	if character == '-' {
		sign = -1
		character, err = r.ReadByte()
	}

	result := int64(0)
	for err == nil && !isSpace(character) {
		result = result*10 + int64(character-'0')
		character, err = r.ReadByte()
	}

	return sign * result
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriter(w)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	// Log(n)
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
			// Log(array[j][i])
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

var (
	in  = bufio.NewScanner(os.Stdin)
	out = bufio.NewWriter(os.Stdout)
)

type point struct {
	x int
	y int
	s int
}

var (
	a   [2500][2500]int
	d1  [5000]int
	d2  [5000]int
	n   int
	tmp point
	ans [2]point
)

func main() {
	defer out.Flush()
	in.Split(bufio.ScanWords)

	read(&n)

	for i := 1; i <= n; i++ {
		for j := 1; j <= n; j++ {
			read(&a[i][j])
		}
	}

	for i := 1; i <= n; i++ {
		for j := 1; j <= n; j++ {
			d1[i+j] += a[i][j]
			d2[n-i+j] += a[i][j]
		}
	}

	for i := 1; i <= n; i++ {
		for j := 1; j <= n; j++ {

			tmp = point{i, j, d1[i+j] + d2[n-i+j] - a[i][j]}

			ans[(i+j)%2] = max(ans[(i+j)%2], tmp)

		}
	}

	fmt.Println(ans[0].s + ans[1].s)
	fmt.Print(ans[0].x, " ", ans[0].y, " ", ans[1].x, " ", ans[1].y)

}

func max(a point, b point) point {
	if a.s > b.s {
		return a
	} else {
		return b
	}
}

func read(num *int) {
	in.Scan()
	*num, _ = strconv.Atoi(in.Text())
}

func writeln(str string) {
	out.WriteString(str + "\n")
}

package main

import (
	"bytes"
	"errors"
	"math/rand"
	"os"
	"strconv"
	"time"
)

const (
	MAX_INPUT_SIZE   int    = 1 << 20
	MAX_STACK_SIZE   int    = 256 * (1 << 20)
	SPACE_CHARACTERS string = " \t\n"
	LINE_SEPARATOR   byte   = '\n'
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")

	input := make([]byte, MAX_INPUT_SIZE)
	inputFile.Read(input)
	input = bytes.Trim(input, SPACE_CHARACTERS)

	inputFile.Close()

	in := NewExtendedBuffer(input)
	out := NewExtendedBuffer([]byte{})

	solve(in, out)

	output := out.Bytes()
	output = bytes.Trim(output, SPACE_CHARACTERS)

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")

	outputFile.Write(output)

	outputFile.Sync()
	outputFile.Close()
}

// IO.

type ExtendedBuffer struct {
	*bytes.Buffer
}

func NewExtendedBuffer(buf []byte) *ExtendedBuffer {
	return &ExtendedBuffer{bytes.NewBuffer(buf)}
}

// Read.

func (in *ExtendedBuffer) ReadWord() string {
	var word []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}

		isSpace := false
		for i := 0; i < len(SPACE_CHARACTERS); i++ {
			if character == byte(SPACE_CHARACTERS[i]) {
				isSpace = true
				break
			}
		}

		if isSpace {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (in *ExtendedBuffer) ReadLine() string {
	line, _ := in.ReadBytes(LINE_SEPARATOR)

	if len(line) != 0 && line[len(line)-1] == LINE_SEPARATOR {
		return string(line[:len(line)-1])
	} else {
		return string(line)
	}
}

func (in *ExtendedBuffer) ReadInt() int {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 0)
	return int(ans)
}

func (in *ExtendedBuffer) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 64)
	return ans
}

func (in *ExtendedBuffer) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(in.ReadWord(), 64)
	return ans
}

// Print.

func (out *ExtendedBuffer) PrintString(value string) {
	out.Write([]byte(value))
}

func (out *ExtendedBuffer) PrintLine() {
	out.PrintString(string(LINE_SEPARATOR))
}

func (out *ExtendedBuffer) PrintSpace() {
	out.PrintString(" ")
}

func (out *ExtendedBuffer) PrintInt(value int) {
	out.PrintString(strconv.FormatInt(int64(value), 10))
}

func (out *ExtendedBuffer) PrintInt64(value int64) {
	out.PrintString(strconv.FormatInt(value, 10))
}

func (out *ExtendedBuffer) PrintFloat64(value float64) {
	out.PrintString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(in *ExtendedBuffer, out *ExtendedBuffer) {
	n = in.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = in.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	out.PrintInt64(best1 + best2)
	out.PrintLine()
	out.PrintInt(x1 + 1)
	out.PrintSpace()
	out.PrintInt(y1 + 1)
	out.PrintSpace()
	out.PrintInt(x2 + 1)
	out.PrintSpace()
	out.PrintInt(y2 + 1)
	out.PrintLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bytes"
	"errors"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_INPUT_SIZE int = 1 << 20
	MAX_STACK_SIZE int = 256 * (1 << 20)
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")

	input := make([]byte, MAX_INPUT_SIZE)
	fileLength, _ := inputFile.Read(input)
	input = input[:fileLength]

	inputFile.Close()

	in := NewExtendedBuffer(input)
	out := NewExtendedBuffer([]byte{})

	solve(in, out)

	output := out.Bytes()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")

	outputFile.Write(output)

	outputFile.Sync()
	outputFile.Close()
}

// IO.

type ExtendedBuffer struct {
	*bytes.Buffer
}

func NewExtendedBuffer(buf []byte) *ExtendedBuffer {
	// Cleans up space characters.
	for i := 0; i < len(buf); i++ {
		if buf[i] != '\n' && unicode.IsSpace(rune(buf[i])) {
			buf[i] = ' '
		}
	}
	return &ExtendedBuffer{bytes.NewBuffer(buf)}
}

// Read.

func (in *ExtendedBuffer) ReadWord() string {
	var word []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}

		if character == ' ' || character == '\n' {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (in *ExtendedBuffer) ReadLine() string {
	line, _ := in.ReadBytes('\n')

	if len(line) != 0 && line[len(line)-1] == '\n' {
		return string(line[:len(line)-1])
	} else {
		return string(line)
	}
}

func (in *ExtendedBuffer) ReadInt() int {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 0)
	return int(ans)
}

func (in *ExtendedBuffer) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 64)
	return ans
}

func (in *ExtendedBuffer) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(in.ReadWord(), 64)
	return ans
}

// Print.

func (out *ExtendedBuffer) PrintString(value string) {
	out.Write([]byte(value))
}

func (out *ExtendedBuffer) PrintLine() {
	out.PrintString("\n")
}

func (out *ExtendedBuffer) PrintSpace() {
	out.PrintString(" ")
}

func (out *ExtendedBuffer) PrintInt(value int) {
	out.PrintString(strconv.FormatInt(int64(value), 10))
}

func (out *ExtendedBuffer) PrintInt64(value int64) {
	out.PrintString(strconv.FormatInt(value, 10))
}

func (out *ExtendedBuffer) PrintFloat64(value float64) {
	out.PrintString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(in *ExtendedBuffer, out *ExtendedBuffer) {
	n = in.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = in.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	out.PrintInt64(best1 + best2)
	out.PrintLine()
	out.PrintInt(x1 + 1)
	out.PrintSpace()
	out.PrintInt(y1 + 1)
	out.PrintSpace()
	out.PrintInt(x2 + 1)
	out.PrintSpace()
	out.PrintInt(y2 + 1)
	out.PrintLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bytes"
	"errors"
	"math/rand"
	"os"
	"strconv"
	"time"
)

const (
	MAX_INPUT_SIZE   int    = 1 << 20
	MAX_STACK_SIZE   int    = 256 * (1 << 20)
	SPACE_CHARACTERS string = " \t\n\v\f\r"
	LINE_SEPARATOR   byte   = '\n'
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")

	input := make([]byte, MAX_INPUT_SIZE)
	fileLength, _ := inputFile.Read(input)
	input = input[:fileLength]
	input = bytes.Trim(input, SPACE_CHARACTERS)

	inputFile.Close()

	in := NewExtendedBuffer(input)
	out := NewExtendedBuffer([]byte{})

	solve(in, out)

	output := out.Bytes()
	output = bytes.Trim(output, SPACE_CHARACTERS)

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")

	outputFile.Write(output)

	outputFile.Sync()
	outputFile.Close()
}

// IO.

type ExtendedBuffer struct {
	*bytes.Buffer
}

func NewExtendedBuffer(buf []byte) *ExtendedBuffer {
	return &ExtendedBuffer{bytes.NewBuffer(buf)}
}

// Read.

func (in *ExtendedBuffer) ReadWord() string {
	var word []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}

		isSpace := false
		for i := 0; i < len(SPACE_CHARACTERS); i++ {
			if character == byte(SPACE_CHARACTERS[i]) {
				isSpace = true
				break
			}
		}

		if isSpace {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (in *ExtendedBuffer) ReadLine() string {
	line, _ := in.ReadBytes(LINE_SEPARATOR)

	if len(line) != 0 && line[len(line)-1] == LINE_SEPARATOR {
		return string(line[:len(line)-1])
	} else {
		return string(line)
	}
}

func (in *ExtendedBuffer) ReadInt() int {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 0)
	return int(ans)
}

func (in *ExtendedBuffer) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 64)
	return ans
}

func (in *ExtendedBuffer) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(in.ReadWord(), 64)
	return ans
}

// Print.

func (out *ExtendedBuffer) PrintString(value string) {
	out.Write([]byte(value))
}

func (out *ExtendedBuffer) PrintLine() {
	out.PrintString(string(LINE_SEPARATOR))
}

func (out *ExtendedBuffer) PrintSpace() {
	out.PrintString(" ")
}

func (out *ExtendedBuffer) PrintInt(value int) {
	out.PrintString(strconv.FormatInt(int64(value), 10))
}

func (out *ExtendedBuffer) PrintInt64(value int64) {
	out.PrintString(strconv.FormatInt(value, 10))
}

func (out *ExtendedBuffer) PrintFloat64(value float64) {
	out.PrintString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(in *ExtendedBuffer, out *ExtendedBuffer) {
	n = in.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = in.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	out.PrintInt64(best1 + best2)
	out.PrintLine()
	out.PrintInt(x1 + 1)
	out.PrintSpace()
	out.PrintInt(y1 + 1)
	out.PrintSpace()
	out.PrintInt(x2 + 1)
	out.PrintSpace()
	out.PrintInt(y2 + 1)
	out.PrintLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bytes"
	"errors"
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_INPUT_SIZE int = 1 << 23
	MAX_STACK_SIZE int = 256 * (1 << 20)
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")

	input := make([]byte, MAX_INPUT_SIZE)
	fileLength, _ := inputFile.Read(input)
	input = input[:fileLength]

	inputFile.Close()

	in := NewExtendedBuffer(input)
	out := NewExtendedBuffer([]byte{})

	solve(in, out)

	output := out.Bytes()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")

	outputFile.Write(output)

	outputFile.Sync()
	outputFile.Close()
}

// IO.

type ExtendedBuffer struct {
	*bytes.Buffer
}

func NewExtendedBuffer(buf []byte) *ExtendedBuffer {
	// Cleans up space characters.
	for i := 0; i < len(buf); i++ {
		if buf[i] != '\n' && unicode.IsSpace(rune(buf[i])) {
			buf[i] = ' '
		}
	}
	return &ExtendedBuffer{bytes.NewBuffer(buf)}
}

// Read.

func (in *ExtendedBuffer) ReadWord() string {
	var word []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}
		if character == ' ' || character == '\n' {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (in *ExtendedBuffer) ReadLine() string {
	var line []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}
		if character == '\n' {
			if len(line) != 0 {
				break
			}
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (in *ExtendedBuffer) ReadInt() int {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 0)
	return int(ans)
}

func (in *ExtendedBuffer) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 64)
	return ans
}

func (in *ExtendedBuffer) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(in.ReadWord(), 64)
	return ans
}

// Print.

func (out *ExtendedBuffer) PrintString(value string) {
	out.Write([]byte(value))
}

func (out *ExtendedBuffer) PrintLine() {
	out.PrintString("\n")
}

func (out *ExtendedBuffer) PrintSpace() {
	out.PrintString(" ")
}

func (out *ExtendedBuffer) PrintInt(value int) {
	out.PrintString(strconv.FormatInt(int64(value), 10))
}

func (out *ExtendedBuffer) PrintUint(value uint) {
	out.PrintString(strconv.FormatUint(uint64(value), 10))
}

func (out *ExtendedBuffer) PrintInt64(value int64) {
	out.PrintString(strconv.FormatInt(value, 10))
}

func (out *ExtendedBuffer) PrintUint64(value uint64) {
	out.PrintString(strconv.FormatUint(value, 10))
}

func (out *ExtendedBuffer) PrintFloat64(value float64) {
	out.PrintString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(in *ExtendedBuffer, out *ExtendedBuffer) {
	n = in.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = in.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	// out.PrintInt64(best1 + best2)
	// out.PrintLine()
	// out.PrintInt(x1 + 1)
	// out.PrintSpace()
	// out.PrintInt(y1 + 1)
	// out.PrintSpace()
	// out.PrintInt(x2 + 1)
	// out.PrintSpace()
	// out.PrintInt(y2 + 1)
	// out.PrintLine()
	fmt.Println(best1 + best2)
	fmt.Print(x1 + 1)
	fmt.Print(" ")
	fmt.Print(y1 + 1)
	fmt.Print(" ")
	fmt.Print(x2 + 1)
	fmt.Print(" ")
	fmt.Println(y2 + 1)
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bytes"
	"errors"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_INPUT_SIZE int = 1 << 20
	MAX_STACK_SIZE int = 256 * (1 << 20)
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")

	input := make([]byte, MAX_INPUT_SIZE)
	fileLength, _ := inputFile.Read(input)
	input = input[:fileLength]

	inputFile.Close()

	in := NewExtendedBuffer(input)
	out := NewExtendedBuffer([]byte{})

	solve(in, out)

	output := out.Bytes()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")

	outputFile.Write(output)

	outputFile.Sync()
	outputFile.Close()
}

// IO.

type ExtendedBuffer struct {
	*bytes.Buffer
}

func NewExtendedBuffer(buf []byte) *ExtendedBuffer {
	// Cleans up space characters.
	for i := 0; i < len(buf); i++ {
		if buf[i] != '\n' && unicode.IsSpace(rune(buf[i])) {
			buf[i] = ' '
		}
	}
	return &ExtendedBuffer{bytes.NewBuffer(buf)}
}

// Read.

func (in *ExtendedBuffer) ReadWord() string {
	var word []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}
		if character == ' ' || character == '\n' {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (in *ExtendedBuffer) ReadLine() string {
	var line []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}
		if character == '\n' {
			if len(line) != 0 {
				break
			}
		} else if character != ' ' {
			line = append(line, character)
		}
	}
	return string(line)
}

func (in *ExtendedBuffer) ReadInt() int {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 0)
	return int(ans)
}

func (in *ExtendedBuffer) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 64)
	return ans
}

func (in *ExtendedBuffer) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(in.ReadWord(), 64)
	return ans
}

// Print.

func (out *ExtendedBuffer) PrintString(value string) {
	out.Write([]byte(value))
}

func (out *ExtendedBuffer) PrintLine() {
	out.PrintString("\n")
}

func (out *ExtendedBuffer) PrintSpace() {
	out.PrintString(" ")
}

func (out *ExtendedBuffer) PrintInt(value int) {
	out.PrintString(strconv.FormatInt(int64(value), 10))
}

func (out *ExtendedBuffer) PrintInt64(value int64) {
	out.PrintString(strconv.FormatInt(value, 10))
}

func (out *ExtendedBuffer) PrintFloat64(value float64) {
	out.PrintString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(in *ExtendedBuffer, out *ExtendedBuffer) {
	n = in.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = in.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	out.PrintInt64(best1 + best2)
	out.PrintLine()
	out.PrintInt(x1 + 1)
	out.PrintSpace()
	out.PrintInt(y1 + 1)
	out.PrintSpace()
	out.PrintInt(x2 + 1)
	out.PrintSpace()
	out.PrintInt(y2 + 1)
	out.PrintLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bytes"
	"errors"
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_INPUT_SIZE int = 1 << 20
	MAX_STACK_SIZE int = 256 * (1 << 20)
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")

	input := make([]byte, MAX_INPUT_SIZE)
	fileLength, _ := inputFile.Read(input)
	input = input[:fileLength]

	inputFile.Close()

	in := NewExtendedBuffer(input)
	out := NewExtendedBuffer([]byte{})

	solve(in, out)

	output := out.Bytes()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")

	outputFile.Write(output)

	outputFile.Sync()
	outputFile.Close()
}

// IO.

type ExtendedBuffer struct {
	*bytes.Buffer
}

func NewExtendedBuffer(buf []byte) *ExtendedBuffer {
	// Cleans up space characters.
	for i := 0; i < len(buf); i++ {
		if buf[i] != '\n' && unicode.IsSpace(rune(buf[i])) {
			buf[i] = ' '
		}
	}
	return &ExtendedBuffer{bytes.NewBuffer(buf)}
}

// Read.

func (in *ExtendedBuffer) ReadWord() string {
	var word []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}
		if character == ' ' || character == '\n' {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (in *ExtendedBuffer) ReadLine() string {
	var line []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}
		if character == '\n' {
			if len(line) != 0 {
				break
			}
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (in *ExtendedBuffer) ReadInt() int {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 0)
	return int(ans)
}

func (in *ExtendedBuffer) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 64)
	return ans
}

func (in *ExtendedBuffer) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(in.ReadWord(), 64)
	return ans
}

// Print.

func (out *ExtendedBuffer) PrintString(value string) {
	out.Write([]byte(value))
}

func (out *ExtendedBuffer) PrintLine() {
	out.PrintString("\n")
}

func (out *ExtendedBuffer) PrintSpace() {
	out.PrintString(" ")
}

func (out *ExtendedBuffer) PrintInt(value int) {
	out.PrintString(strconv.FormatInt(int64(value), 10))
}

func (out *ExtendedBuffer) PrintUint(value uint) {
	out.PrintString(strconv.FormatUint(uint64(value), 10))
}

func (out *ExtendedBuffer) PrintInt64(value int64) {
	out.PrintString(strconv.FormatInt(value, 10))
}

func (out *ExtendedBuffer) PrintUint64(value uint64) {
	out.PrintString(strconv.FormatUint(value, 10))
}

func (out *ExtendedBuffer) PrintFloat64(value float64) {
	out.PrintString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(in *ExtendedBuffer, out *ExtendedBuffer) {
	n = in.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = in.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	// out.PrintInt64(best1 + best2)
	// out.PrintLine()
	// out.PrintInt(x1 + 1)
	// out.PrintSpace()
	// out.PrintInt(y1 + 1)
	// out.PrintSpace()
	// out.PrintInt(x2 + 1)
	// out.PrintSpace()
	// out.PrintInt(y2 + 1)
	// out.PrintLine()
	fmt.Println(best1 + best2)
	fmt.Print(x1 + 1)
	fmt.Print(" ")
	fmt.Print(y1 + 1)
	fmt.Print(" ")
	fmt.Print(x2 + 1)
	fmt.Print(" ")
	fmt.Println(y2 + 1)
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bytes"
	"errors"
	"math/rand"
	"os"
	"strconv"
	"time"
)

const (
	MAX_INPUT_SIZE   int    = 1 << 20
	MAX_STACK_SIZE   int    = 256 * (1 << 20)
	SPACE_CHARACTERS string = " \t\n\v\f\r"
	LINE_SEPARATOR   byte   = '\n'
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")

	input := make([]byte, MAX_INPUT_SIZE)
	inputFile.Read(input)
	input = bytes.Trim(input, SPACE_CHARACTERS)

	inputFile.Close()

	in := NewExtendedBuffer(input)
	out := NewExtendedBuffer([]byte{})

	solve(in, out)

	output := out.Bytes()
	output = bytes.Trim(output, SPACE_CHARACTERS)

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")

	outputFile.Write(output)

	outputFile.Sync()
	outputFile.Close()
}

// IO.

type ExtendedBuffer struct {
	*bytes.Buffer
}

func NewExtendedBuffer(buf []byte) *ExtendedBuffer {
	return &ExtendedBuffer{bytes.NewBuffer(buf)}
}

// Read.

func (in *ExtendedBuffer) ReadWord() string {
	var word []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}

		isSpace := false
		for i := 0; i < len(SPACE_CHARACTERS); i++ {
			if character == byte(SPACE_CHARACTERS[i]) {
				isSpace = true
				break
			}
		}

		if isSpace {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (in *ExtendedBuffer) ReadLine() string {
	line, _ := in.ReadBytes(LINE_SEPARATOR)

	if len(line) != 0 && line[len(line)-1] == LINE_SEPARATOR {
		return string(line[:len(line)-1])
	} else {
		return string(line)
	}
}

func (in *ExtendedBuffer) ReadInt() int {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 0)
	return int(ans)
}

func (in *ExtendedBuffer) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 64)
	return ans
}

func (in *ExtendedBuffer) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(in.ReadWord(), 64)
	return ans
}

// Print.

func (out *ExtendedBuffer) PrintString(value string) {
	out.Write([]byte(value))
}

func (out *ExtendedBuffer) PrintLine() {
	out.PrintString(string(LINE_SEPARATOR))
}

func (out *ExtendedBuffer) PrintSpace() {
	out.PrintString(" ")
}

func (out *ExtendedBuffer) PrintInt(value int) {
	out.PrintString(strconv.FormatInt(int64(value), 10))
}

func (out *ExtendedBuffer) PrintInt64(value int64) {
	out.PrintString(strconv.FormatInt(value, 10))
}

func (out *ExtendedBuffer) PrintFloat64(value float64) {
	out.PrintString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(in *ExtendedBuffer, out *ExtendedBuffer) {
	n = in.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = in.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	out.PrintInt64(best1 + best2)
	out.PrintLine()
	out.PrintInt(x1 + 1)
	out.PrintSpace()
	out.PrintInt(y1 + 1)
	out.PrintSpace()
	out.PrintInt(x2 + 1)
	out.PrintSpace()
	out.PrintInt(y2 + 1)
	out.PrintLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bytes"
	"errors"
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_INPUT_SIZE int = 1 << 24
	MAX_STACK_SIZE int = 256 * (1 << 20)
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")

	input := make([]byte, MAX_INPUT_SIZE)
	fileLength, _ := inputFile.Read(input)
	input = input[:fileLength]

	inputFile.Close()

	in := NewExtendedBuffer(input)
	out := NewExtendedBuffer([]byte{})

	solve(in, out)

	output := out.Bytes()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")

	outputFile.Write(output)

	outputFile.Sync()
	outputFile.Close()
}

// IO.

type ExtendedBuffer struct {
	*bytes.Buffer
}

func NewExtendedBuffer(buf []byte) *ExtendedBuffer {
	// Cleans up space characters.
	for i := 0; i < len(buf); i++ {
		if buf[i] != '\n' && unicode.IsSpace(rune(buf[i])) {
			buf[i] = ' '
		}
	}
	return &ExtendedBuffer{bytes.NewBuffer(buf)}
}

// Read.

func (in *ExtendedBuffer) ReadWord() string {
	var word []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}
		if character == ' ' || character == '\n' {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (in *ExtendedBuffer) ReadLine() string {
	var line []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}
		if character == '\n' {
			if len(line) != 0 {
				break
			}
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (in *ExtendedBuffer) ReadInt() int {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 0)
	return int(ans)
}

func (in *ExtendedBuffer) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 64)
	return ans
}

func (in *ExtendedBuffer) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(in.ReadWord(), 64)
	return ans
}

// Print.

func (out *ExtendedBuffer) PrintString(value string) {
	out.Write([]byte(value))
}

func (out *ExtendedBuffer) PrintLine() {
	out.PrintString("\n")
}

func (out *ExtendedBuffer) PrintSpace() {
	out.PrintString(" ")
}

func (out *ExtendedBuffer) PrintInt(value int) {
	out.PrintString(strconv.FormatInt(int64(value), 10))
}

func (out *ExtendedBuffer) PrintUint(value uint) {
	out.PrintString(strconv.FormatUint(uint64(value), 10))
}

func (out *ExtendedBuffer) PrintInt64(value int64) {
	out.PrintString(strconv.FormatInt(value, 10))
}

func (out *ExtendedBuffer) PrintUint64(value uint64) {
	out.PrintString(strconv.FormatUint(value, 10))
}

func (out *ExtendedBuffer) PrintFloat64(value float64) {
	out.PrintString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(in *ExtendedBuffer, out *ExtendedBuffer) {
	n = in.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = in.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	// out.PrintInt64(best1 + best2)
	// out.PrintLine()
	// out.PrintInt(x1 + 1)
	// out.PrintSpace()
	// out.PrintInt(y1 + 1)
	// out.PrintSpace()
	// out.PrintInt(x2 + 1)
	// out.PrintSpace()
	// out.PrintInt(y2 + 1)
	// out.PrintLine()
	fmt.Println(best1 + best2)
	fmt.Print(x1 + 1)
	fmt.Print(" ")
	fmt.Print(y1 + 1)
	fmt.Print(" ")
	fmt.Print(x2 + 1)
	fmt.Print(" ")
	fmt.Println(y2 + 1)
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bytes"
	"errors"
	"math/rand"
	"os"
	"strconv"
	"time"
)

const (
	MAX_INPUT_SIZE   int    = 1 << 20
	MAX_STACK_SIZE   int    = 256 * (1 << 20)
	SPACE_CHARACTERS string = " \t\n\r"
	LINE_SEPARATOR   byte   = '\n'
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")

	input := make([]byte, MAX_INPUT_SIZE)
	inputFile.Read(input)
	input = bytes.Trim(input, SPACE_CHARACTERS)

	inputFile.Close()

	in := NewExtendedBuffer(input)
	out := NewExtendedBuffer([]byte{})

	solve(in, out)

	output := out.Bytes()
	output = bytes.Trim(output, SPACE_CHARACTERS)

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")

	outputFile.Write(output)

	outputFile.Sync()
	outputFile.Close()
}

// IO.

type ExtendedBuffer struct {
	*bytes.Buffer
}

func NewExtendedBuffer(buf []byte) *ExtendedBuffer {
	return &ExtendedBuffer{bytes.NewBuffer(buf)}
}

// Read.

func (in *ExtendedBuffer) ReadWord() string {
	var word []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}

		isSpace := false
		for i := 0; i < len(SPACE_CHARACTERS); i++ {
			if character == byte(SPACE_CHARACTERS[i]) {
				isSpace = true
				break
			}
		}

		if isSpace {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (in *ExtendedBuffer) ReadLine() string {
	line, _ := in.ReadBytes(LINE_SEPARATOR)

	if len(line) != 0 && line[len(line)-1] == LINE_SEPARATOR {
		return string(line[:len(line)-1])
	} else {
		return string(line)
	}
}

func (in *ExtendedBuffer) ReadInt() int {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 0)
	return int(ans)
}

func (in *ExtendedBuffer) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 64)
	return ans
}

func (in *ExtendedBuffer) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(in.ReadWord(), 64)
	return ans
}

// Print.

func (out *ExtendedBuffer) PrintString(value string) {
	out.Write([]byte(value))
}

func (out *ExtendedBuffer) PrintLine() {
	out.PrintString(string(LINE_SEPARATOR))
}

func (out *ExtendedBuffer) PrintSpace() {
	out.PrintString(" ")
}

func (out *ExtendedBuffer) PrintInt(value int) {
	out.PrintString(strconv.FormatInt(int64(value), 10))
}

func (out *ExtendedBuffer) PrintInt64(value int64) {
	out.PrintString(strconv.FormatInt(value, 10))
}

func (out *ExtendedBuffer) PrintFloat64(value float64) {
	out.PrintString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(in *ExtendedBuffer, out *ExtendedBuffer) {
	n = in.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = in.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	out.PrintInt64(best1 + best2)
	out.PrintLine()
	out.PrintInt(x1 + 1)
	out.PrintSpace()
	out.PrintInt(y1 + 1)
	out.PrintSpace()
	out.PrintInt(x2 + 1)
	out.PrintSpace()
	out.PrintInt(y2 + 1)
	out.PrintLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bytes"
	"errors"
	"math/rand"
	"os"
	"strconv"
	"time"
)

const (
	MAX_INPUT_SIZE   int    = 1 << 23
	MAX_STACK_SIZE   int    = 256 * (1 << 20)
	SPACE_CHARACTERS string = " \t\n\v\f\r"
	LINE_SEPARATOR   byte   = '\n'
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")

	input := make([]byte, MAX_INPUT_SIZE)
	inputFile.Read(input)
	input = bytes.Trim(input, SPACE_CHARACTERS)

	inputFile.Close()

	in := NewExtendedBuffer(input)
	out := NewExtendedBuffer([]byte{})

	solve(in, out)

	output := out.Bytes()
	output = bytes.Trim(output, SPACE_CHARACTERS)

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")

	outputFile.Write(output)

	outputFile.Sync()
	outputFile.Close()
}

// IO.

type ExtendedBuffer struct {
	*bytes.Buffer
}

func NewExtendedBuffer(buf []byte) *ExtendedBuffer {
	return &ExtendedBuffer{bytes.NewBuffer(buf)}
}

// Read.

func (in *ExtendedBuffer) ReadWord() string {
	var word []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}

		isSpace := false
		for i := 0; i < len(SPACE_CHARACTERS); i++ {
			if character == byte(SPACE_CHARACTERS[i]) {
				isSpace = true
				break
			}
		}

		if isSpace {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (in *ExtendedBuffer) ReadLine() string {
	line, _ := in.ReadBytes(LINE_SEPARATOR)

	if len(line) != 0 && line[len(line)-1] == LINE_SEPARATOR {
		return string(line[:len(line)-1])
	} else {
		return string(line)
	}
}

func (in *ExtendedBuffer) ReadInt() int {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 0)
	return int(ans)
}

func (in *ExtendedBuffer) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 64)
	return ans
}

func (in *ExtendedBuffer) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(in.ReadWord(), 64)
	return ans
}

// Print.

func (out *ExtendedBuffer) PrintString(value string) {
	out.Write([]byte(value))
}

func (out *ExtendedBuffer) PrintLine() {
	out.PrintString(string(LINE_SEPARATOR))
}

func (out *ExtendedBuffer) PrintSpace() {
	out.PrintString(" ")
}

func (out *ExtendedBuffer) PrintInt(value int) {
	out.PrintString(strconv.FormatInt(int64(value), 10))
}

func (out *ExtendedBuffer) PrintInt64(value int64) {
	out.PrintString(strconv.FormatInt(value, 10))
}

func (out *ExtendedBuffer) PrintFloat64(value float64) {
	out.PrintString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(in *ExtendedBuffer, out *ExtendedBuffer) {
	n = in.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = in.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	out.PrintInt64(best1 + best2)
	out.PrintLine()
	out.PrintInt(x1 + 1)
	out.PrintSpace()
	out.PrintInt(y1 + 1)
	out.PrintSpace()
	out.PrintInt(x2 + 1)
	out.PrintSpace()
	out.PrintInt(y2 + 1)
	out.PrintLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bytes"
	"errors"
	"math/rand"
	"os"
	"strconv"
	"time"
)

const (
	MAX_INPUT_SIZE   int    = 1 << 20
	MAX_STACK_SIZE   int    = 256 * (1 << 20)
	SPACE_CHARACTERS string = " \t\n\v\f\r"
	LINE_SEPARATOR   byte   = '\n'
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")

	input := make([]byte, MAX_INPUT_SIZE)
	inputFile.Read(input)
	// input = bytes.Trim(input, SPACE_CHARACTERS)

	inputFile.Close()

	in := NewExtendedBuffer(input)
	out := NewExtendedBuffer([]byte{})

	solve(in, out)

	output := out.Bytes()
	// output = bytes.Trim(output, SPACE_CHARACTERS)

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")

	outputFile.Write(output)

	outputFile.Sync()
	outputFile.Close()
}

// IO.

type ExtendedBuffer struct {
	*bytes.Buffer
}

func NewExtendedBuffer(buf []byte) *ExtendedBuffer {
	return &ExtendedBuffer{bytes.NewBuffer(buf)}
}

// Read.

func (in *ExtendedBuffer) ReadWord() string {
	var word []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}

		isSpace := false
		for i := 0; i < len(SPACE_CHARACTERS); i++ {
			if character == byte(SPACE_CHARACTERS[i]) {
				isSpace = true
				break
			}
		}

		if isSpace {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (in *ExtendedBuffer) ReadLine() string {
	line, _ := in.ReadBytes(LINE_SEPARATOR)

	if len(line) != 0 && line[len(line)-1] == LINE_SEPARATOR {
		return string(line[:len(line)-1])
	} else {
		return string(line)
	}
}

func (in *ExtendedBuffer) ReadInt() int {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 0)
	return int(ans)
}

func (in *ExtendedBuffer) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 64)
	return ans
}

func (in *ExtendedBuffer) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(in.ReadWord(), 64)
	return ans
}

// Print.

func (out *ExtendedBuffer) PrintString(value string) {
	out.Write([]byte(value))
}

func (out *ExtendedBuffer) PrintLine() {
	out.PrintString(string(LINE_SEPARATOR))
}

func (out *ExtendedBuffer) PrintSpace() {
	out.PrintString(" ")
}

func (out *ExtendedBuffer) PrintInt(value int) {
	out.PrintString(strconv.FormatInt(int64(value), 10))
}

func (out *ExtendedBuffer) PrintInt64(value int64) {
	out.PrintString(strconv.FormatInt(value, 10))
}

func (out *ExtendedBuffer) PrintFloat64(value float64) {
	out.PrintString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(in *ExtendedBuffer, out *ExtendedBuffer) {
	n = in.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = in.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	out.PrintInt64(best1 + best2)
	out.PrintLine()
	out.PrintInt(x1 + 1)
	out.PrintSpace()
	out.PrintInt(y1 + 1)
	out.PrintSpace()
	out.PrintInt(x2 + 1)
	out.PrintSpace()
	out.PrintInt(y2 + 1)
	out.PrintLine()
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bytes"
	"errors"
	"fmt"
	"math/rand"
	// "os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_INPUT_SIZE int = 1 << 20
	MAX_STACK_SIZE int = 256 * (1 << 20)
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	// inputFile := os.Stdin
	// inputFile, _ := os.Open("")

	input := make([]byte, MAX_INPUT_SIZE)
	// fileLength, _ := inputFile.Read(input)
	// input = input[:fileLength]

	// inputFile.Close()

	in := NewExtendedBuffer(input)
	out := NewExtendedBuffer([]byte{})

	solve(in, out)

	// output := out.Bytes()

	// outputFile := os.Stdout
	// outputFile, _ := os.Create("")

	// outputFile.Write(output)

	// outputFile.Sync()
	// outputFile.Close()
}

// IO.

type ExtendedBuffer struct {
	*bytes.Buffer
}

func NewExtendedBuffer(buf []byte) *ExtendedBuffer {
	// Cleans up space characters.
	for i := 0; i < len(buf); i++ {
		if buf[i] != '\n' && unicode.IsSpace(rune(buf[i])) {
			buf[i] = ' '
		}
	}
	return &ExtendedBuffer{bytes.NewBuffer(buf)}
}

// Read.

func (in *ExtendedBuffer) ReadWord() string {
	var word []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}
		if character == ' ' || character == '\n' {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (in *ExtendedBuffer) ReadLine() string {
	var line []byte
	for {
		character, err := in.ReadByte()
		if err != nil {
			break
		}
		if character == '\n' {
			if len(line) != 0 {
				break
			}
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (in *ExtendedBuffer) ReadInt() int {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 0)
	return int(ans)
}

func (in *ExtendedBuffer) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(in.ReadWord(), 10, 64)
	return ans
}

func (in *ExtendedBuffer) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(in.ReadWord(), 64)
	return ans
}

// Print.

func (out *ExtendedBuffer) PrintString(value string) {
	out.Write([]byte(value))
}

func (out *ExtendedBuffer) PrintLine() {
	out.PrintString("\n")
}

func (out *ExtendedBuffer) PrintSpace() {
	out.PrintString(" ")
}

func (out *ExtendedBuffer) PrintInt(value int) {
	out.PrintString(strconv.FormatInt(int64(value), 10))
}

func (out *ExtendedBuffer) PrintUint(value uint) {
	out.PrintString(strconv.FormatUint(uint64(value), 10))
}

func (out *ExtendedBuffer) PrintInt64(value int64) {
	out.PrintString(strconv.FormatInt(value, 10))
}

func (out *ExtendedBuffer) PrintUint64(value uint64) {
	out.PrintString(strconv.FormatUint(value, 10))
}

func (out *ExtendedBuffer) PrintFloat64(value float64) {
	out.PrintString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(in *ExtendedBuffer, out *ExtendedBuffer) {
	// n = in.ReadInt()
	fmt.Scanf("%d", &n)
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			fmt.Scanf("%d", &array[j][i])
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	// out.PrintInt64(best1 + best2)
	// out.PrintLine()
	// out.PrintInt(x1 + 1)
	// out.PrintSpace()
	// out.PrintInt(y1 + 1)
	// out.PrintSpace()
	// out.PrintInt(x2 + 1)
	// out.PrintSpace()
	// out.PrintInt(y2 + 1)
	// out.PrintLine()
	fmt.Println(best1 + best2)
	fmt.Print(x1 + 1)
	fmt.Print(" ")
	fmt.Print(y1 + 1)
	fmt.Print(" ")
	fmt.Print(x2 + 1)
	fmt.Print(" ")
	fmt.Println(y2 + 1)
}

// ********************************* CODE ENDS *********************************

package main

import (
	"bufio"
	"errors"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"
	"unicode"
)

const (
	MAX_STACK_SIZE  int = 256 * (1 << 20)
	MIN_BUFFER_SIZE int = 1 << 20
)

func main() {
	// debug.SetMaxStack(MAX_STACK_SIZE)
	rand.Seed(int64(time.Now().Nanosecond()))

	inputFile := os.Stdin
	// inputFile, _ := os.Open("")
	defer inputFile.Close()

	outputFile := os.Stdout
	// outputFile, _ := os.Create("")
	defer outputFile.Close()
	defer outputFile.Sync()

	r := NewReader(inputFile)
	w := NewWriter(outputFile)
	defer w.Flush()

	solve(r, w)
}

// Reader.

type Reader struct {
	*bufio.Reader
}

func NewReader(r io.Reader) *Reader {
	return &Reader{bufio.NewReaderSize(r, MIN_BUFFER_SIZE)}
}

func (r *Reader) ReadWord() string {
	var word []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if character == ' ' || character == '\n' {
			if len(word) != 0 {
				break
			}
		} else {
			word = append(word, character)
		}
	}
	return string(word)
}

func (r *Reader) ReadLine() string {
	var line []byte
	for {
		character, err := r.ReadByte()
		if err != nil {
			break
		}
		if unicode.IsSpace(rune(character)) &&
			character != '\n' && character != '\t' && character != ' ' {
			continue
		}
		if character == '\n' {
			break
		} else {
			line = append(line, character)
		}
	}
	return string(line)
}

func (r *Reader) ReadInt() int {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 0)
	return int(ans)
}

func (r *Reader) ReadInt64() int64 {
	ans, _ := strconv.ParseInt(r.ReadWord(), 10, 64)
	return ans
}

func (r *Reader) ReadFloat64() float64 {
	ans, _ := strconv.ParseFloat(r.ReadWord(), 64)
	return ans
}

// Writer.

type Writer struct {
	*bufio.Writer
}

func NewWriter(w io.Writer) *Writer {
	return &Writer{bufio.NewWriterSize(w, MIN_BUFFER_SIZE)}
}

func (w *Writer) WriteString(value string) {
	w.Write([]byte(value))
}

func (w *Writer) WriteLine() {
	w.WriteString("\n")
}

func (w *Writer) WriteSpace() {
	w.WriteString(" ")
}

func (w *Writer) WriteInt(value int) {
	w.WriteString(strconv.FormatInt(int64(value), 10))
}

func (w *Writer) WriteUint(value uint) {
	w.WriteString(strconv.FormatUint(uint64(value), 10))
}

func (w *Writer) WriteInt64(value int64) {
	w.WriteString(strconv.FormatInt(value, 10))
}

func (w *Writer) WriteUint64(value uint64) {
	w.WriteString(strconv.FormatUint(value, 10))
}

func (w *Writer) WriteFloat64(value float64) {
	w.WriteString(strconv.FormatFloat(value, 'f', 20, 64))
}

// Tuple

type Tuple struct {
	a, b int
}

func (p Tuple) Equals(key Key) bool {
	return p.a == key.(Tuple).a
}

func (p Tuple) Less(key Key) bool {
	return p.a < key.(Tuple).a
}

// Sorting

type TupleSlice []Tuple

func (p TupleSlice) Len() int {
	return len(p)
}

func (p TupleSlice) Less(j, i int) bool {
	if p[j].a == p[i].a {
		return p[j].b < p[i].b
	} else {
		return p[j].a < p[i].a
	}
}

func (p TupleSlice) Swap(j, i int) {
	p[j], p[i] = p[i], p[j]
}

// Min, max, abs.

func maxInt(a, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt(a int) int {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxInt64(a, b int64) int64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minInt64(a, b int64) int64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absInt64(a int64) int64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

func maxFloat64(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func minFloat64(a, b float64) float64 {
	if a < b {
		return a
	} else {
		return b
	}
}

func absFloat64(a float64) float64 {
	if a > 0 {
		return a
	} else {
		return -a
	}
}

// Set Key interface implementations.

type Int int

func (p Int) Equals(key Key) bool {
	return int(p) == int(key.(Int))
}

func (p Int) Less(key Key) bool {
	return int(p) < int(key.(Int))
}

// Set.

type Key interface {
	Equals(key Key) bool
	Less(key Key) bool
}

type node struct {
	key   Key
	left  *node
	right *node
}

func newNode(key Key) *node {
	return &node{key, nil, nil}
}

type SplayTree struct {
	root *node
	tmp  *node
	len  int
}

func NewSplayTree() *SplayTree {
	return &SplayTree{nil, newNode(nil), 0}
}

func rotateLeft(x, p *node) {
	p.right = x.left
	x.left = p
}

func rotateRight(x, p *node) {
	p.left = x.right
	x.right = p
}

func (p *SplayTree) splay(x *node, key Key) *node {
	if x == nil {
		return nil
	}

	left := p.tmp
	right := p.tmp

	for {
		if key.Less(x.key) {
			y := x.left
			if y == nil {
				break
			}
			if key.Less(y.key) { // zig-zig
				rotateRight(y, x)
				x = y
				if x.left == nil {
					break
				}
			}
			// link right
			right.left = x
			right = x
			// move left
			x = x.left
		} else if x.key.Less(key) {
			y := x.right
			if y == nil {
				break
			}
			if y.key.Less(key) { // zig-zig
				rotateLeft(y, x)
				x = y
				if x.right == nil {
					break
				}
			}
			// link left
			left.right = x
			left = x
			// move right
			x = x.right
		} else {
			break
		}
	}

	left.right = x.left
	right.left = x.right
	x.left = p.tmp.right
	x.right = p.tmp.left

	return x
}

// left <= key < right
func (p *SplayTree) split(key Key) (left, right *node) {
	p.root = p.splay(p.root, key)
	if p.root.key.Equals(key) || p.root.key.Less(key) {
		right := p.root.right
		p.root.right = nil
		return p.root, right
	} else {
		left := p.root.left
		p.root.left = nil
		return left, p.root
	}
}

// keys from left tree must be less then keys from right tree
func (p *SplayTree) join(left, right *node) *node {
	if left == nil {
		return right
	} else if right == nil {
		return left
	}
	left = p.splay(left, right.key)
	left.right = right
	return left
}

type Set interface {
	Len() int
	Insert(key Key) error
	Find(key Key) bool
	Remove(key Key) error
}

func NewSet() Set {
	return Set(NewSplayTree())
}

func (p *SplayTree) Len() int {
	return p.len
}

func (p *SplayTree) Insert(key Key) error {
	if p.root == nil {
		p.root = newNode(key)
		p.len++
	} else {
		p.root = p.splay(p.root, key)
		if p.root.key.Equals(key) {
			return errors.New("Such key already exists")
		} else {
			left, right := p.split(key)
			p.root = newNode(key)
			p.root.left = left
			p.root.right = right
			p.len++
		}
	}
	return nil
}

func (p *SplayTree) Find(key Key) bool {
	if p.root == nil {
		return false
	}
	p.root = p.splay(p.root, key)
	return p.root.key.Equals(key)
}

func (p *SplayTree) Remove(key Key) error {
	p.root = p.splay(p.root, key)
	if p.root == nil || !p.root.key.Equals(key) {
		return errors.New("Such key doesn't exist")
	}
	p.root = p.join(p.split(key))
	return nil
}

// ******************************** CODE STARTS ********************************

const ()

var (
	n     int
	d1    [1 << 12]int64
	d2    [1 << 12]int64
	array [1 << 11][1 << 11]int
)

func calc1(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j--
		i++
	}
	d1[sj+si] = res
}

func calc2(sj, si int) {
	res := int64(0)
	j := sj
	i := si
	for j >= 0 && i >= 0 && j < n && i < n {
		res += int64(array[j][i])
		j++
		i++
	}
	d2[(1<<11)+sj-si] = res
}

func solve(r *Reader, w *Writer) {
	n = r.ReadInt()
	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			array[j][i] = r.ReadInt()
		}
	}
	for i := 0; i < n; i++ {
		calc1(i, 0)
		calc2(i, 0)

		calc1(n-1, i)
		calc2(0, i)
	}

	best1 := int64(0)
	x1 := -1
	y1 := -1
	best2 := int64(0)
	x2 := -1
	y2 := -1

	for j := 0; j < n; j++ {
		for i := 0; i < n; i++ {
			if ((j + i) & 1) == 1 {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best1 <= value {
					best1 = value
					x1 = j
					y1 = i
				}
			} else {
				value := d1[j+i] + d2[(1<<11)+j-i] - int64(array[j][i])
				if best2 <= value {
					best2 = value
					x2 = j
					y2 = i
				}
			}
		}
	}
	w.WriteInt64(best1 + best2)
	w.WriteLine()
	w.WriteInt(x1 + 1)
	w.WriteSpace()
	w.WriteInt(y1 + 1)
	w.WriteSpace()
	w.WriteInt(x2 + 1)
	w.WriteSpace()
	w.WriteInt(y2 + 1)
	w.WriteLine()
}

// ********************************* CODE ENDS *********************************

package main

import "fmt"

type Value struct {
	x, y, val int
}

func main() {
	var n int

	_, _ = fmt.Scanln(&n)

	matrix := make([][]int, n)
	mapLR := make(map[int]int)
	mapRL := make(map[int]int)

	for i := 0; i < n; i++ {
		matrix[i] = make([]int, n)
		for j := 0; j < n; j++ {
			_, _ = fmt.Scan(&matrix[i][j])
			mapRL[i+j] += matrix[i][j]
			mapLR[i-j] += matrix[i][j]
		}
	}

	big := Value{val: -1}
	sbig := Value{val: -1}

	for i:= 0; i < n; i++ {
		for j := 0; j < n; j++ {
			curVal := mapRL[i+j] + mapLR[i-j] - matrix[i][j]
			if big.val < curVal {
				sbig = big
				big = Value{i,j,curVal}
			} else if sbig.val < curVal {
				sbig = Value{i,j,curVal}
			}
		}
	}
	fmt.Println(big.val + sbig.val)
	fmt.Println(big.x+1, big.y+1, sbig.x+1, sbig.y+1)
}

package main

import (
	"bufio"
	"fmt"
	"os"
)

type Value struct {
	x, y, val int
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)
	defer writer.Flush()

	var n int
	_, _ = fmt.Fscanf(reader, "%d\n", &n)

	matrix := make([][]int, n)
	mapLR := [4010]int{}
	mapRL := [4010]int{}

	for i := 0; i < n; i++ {
		matrix[i] = make([]int, n)
		for j := 0; j < n; j++ {
			_, _ = fmt.Fscanf(reader, "%d", &matrix[i][j])
			mapRL[i+j] += matrix[i][j]
			mapLR[i-j+n] += matrix[i][j]
		}
		_, _ = fmt.Fscanf(reader, "\n")
	}

	bc := make([]Value, 2,)
	bc[0].val = -1
	bc[1].val = -1

	for i:= 0; i < n; i++ {
		for j := 0; j < n; j++ {
			curVal := mapRL[i+j] + mapLR[i-j+n] - matrix[i][j]
			e:= (i+j)&1
			if bc[e].val < curVal {
				bc[e] =  Value{i,j,curVal}
			}
		}
	}
	_, _ = fmt.Fprintf(writer, "%d\n", bc[0].val+bc[1].val)
	_, _ = fmt.Fprintf(writer, "%d %d %d %d\n", bc[0].x+1, bc[0].y+1, bc[1].x+1, bc[1].y+1)
}

// Author: sighduck
// URL: https://codeforces.com/problemset/problem/463/C

package main

import (
	"bufio"
	"fmt"
	"os"
)

type Bishop struct {
	X int
	Y int
}

type Solution struct {
	Cost int64
	B1   Bishop
	B2   Bishop
}

func Solve(n int, board [][]int64) Solution {
	d1, d2 := make(map[int]int64), make(map[int]int64)
	for i := 0; i < n; i++ {
		for j := 0; j < n; j++ {
			d1[i+j] += board[i][j]
			d2[i-j+n] += board[i][j]
		}
	}
	maxEvenDiagonal, maxOddDiagonal := int64(0), int64(0)
	maxEvenBishop, maxOddBishop := Bishop{}, Bishop{}
	for i := 0; i < n; i++ {
		for j := 0; j < n; j++ {
			diagonalSum := d1[i+j] + d2[i-j+n] - board[i][j]
			if (i+j)%2 == 0 && diagonalSum > maxEvenDiagonal {
				maxEvenDiagonal = diagonalSum
				maxEvenBishop = Bishop{i + 1, j + 1}
			} else if (i+j)%2 == 1 && diagonalSum > maxOddDiagonal {
				maxOddDiagonal = diagonalSum
				maxOddBishop = Bishop{i + 1, j + 1}
			}
		}
	}
	return Solution{Cost: maxEvenDiagonal + maxOddDiagonal, B1: maxEvenBishop, B2: maxOddBishop}
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)
	defer writer.Flush()

	var n int
	fmt.Fscanf(reader, "%d\n", &n)
	board := make([][]int64, n)
	for i := 0; i < n; i++ {
		board[i] = make([]int64, n)
		for j := 0; j < n; j++ {
			fmt.Fscanf(reader, "%d", &board[i][j])
		}
		fmt.Fscanf(reader, "\n")
	}

	solution := Solve(n, board)
	fmt.Fprintf(writer, "%d\n", solution.Cost)
	fmt.Fprintf(writer, "%d %d %d %d\n", solution.B1.X, solution.B1.Y, solution.B2.X, solution.B2.Y)
}