#include <iostream>
#include <stdio.h>
#include <set>
#include <vector>
#include <queue>
using namespace std;

#define minus(x, y) (x > y ? x - y :  y - x)

uint64_t GetFinal()
{
	uint64_t res = 0;
	for(int i = 0; i < 16; i++)
	{
		uint64_t iBit = (i+1) & 0xf;
		//printf("%d\n", iBit);
		res |= iBit << 60 - i*4;
	}
	return res;
}

void PrintVal(uint64_t iVal)
{
	for(int i = 0 ;i < 4; i++)
	{
		for(int j = 0; j < 4; j++)
		{
			uint64_t iMask = 0xf;
			int shift = 60 - 4*(4*i + j);
			int digit = (iVal >> shift) & iMask;
			printf("%d ", digit);
		}
		printf("\n");
	}
}

uint64_t Shift(uint64_t iVal, int iLft, int iRgt)
{
	uint64_t iMask = 0xf;
	uint64_t iDst = (iVal & (iMask << (60 - iRgt*4)));
	if(iLft < iRgt)
		iDst = iDst << (4 * (iRgt - iLft));
	else
		iDst = iDst >> (4 * (iLft - iRgt));
	//printf("dst %d\n", iDst);

	iVal &= ~(iMask << (60 - iRgt*4)); //抹除
	iVal |= iDst;
	return iVal;
}

uint64_t L(uint64_t iVal, int iZeroIdx)
{
	return Shift(iVal, iZeroIdx, iZeroIdx - 1);
}

uint64_t R(uint64_t iVal, int iZeroIdx)
{
	return Shift(iVal, iZeroIdx, iZeroIdx + 1);
}

uint64_t U(uint64_t iVal, int iZeroIdx)
{
	return Shift(iVal, iZeroIdx, iZeroIdx - 4);
}

uint64_t D(uint64_t iVal, int iZeroIdx)
{
	return Shift(iVal, iZeroIdx, iZeroIdx + 4);
}

enum eDirection
{
	eUp = 0x01,
	eDown = 0x02,
	eLeft = 0x04,
	eRight = 0x08,
};

struct Node
{	
	uint64_t iFromVal;
	uint32_t iZeroIdx;
	int iFrom;
	/*
	int iDiff;
	int iStep;

	friend bool operator< (const Node& a, const Node& b)
	{
		return a.iDiff > b.iDiff;
	};
	*/
};

int FindDirection(int iZeroIdx)
{
	int ret = 0;
	if(iZeroIdx % 4 != 0)
		ret |= eLeft;

	if((iZeroIdx + 1) % 4 != 0)
		ret |= eRight;

	if(3 < iZeroIdx && iZeroIdx < 16)
		ret |= eUp;

	if(0 <= iZeroIdx && iZeroIdx < 12)
		ret |= eDown;

	return ret;
}

bool CanSolve(uint64_t iVal)
{
	uint64_t iMask = 0xf;
	int sum = 0;
	int iZeroRow = 0;
	for(int i = 0; i < 16; i++)
	{
		uint64_t iLft = (iVal & (iMask << 60 - 4*i)) >> (60 - 4*i);
		if(iLft == 0)
		{
	//		printf("zero pos %d\n", i);
			iZeroRow = i / 4;
		}
		if(i == 15)
			break;
		for(int j = i + 1; j < 16; j++)
		{
			uint64_t iRgt = (iVal & (iMask << 60 - 4*j)) >> (60 - 4*j);
			if(iLft != 0 && iRgt != 0 && iRgt < iLft)
				sum ++;
		}
	}

//	printf("zero row %d nixu pair %d\n", iZeroRow, sum);
	
	if(iZeroRow % 2)
		return sum%2 == 0;

	return sum % 2 == 1;
}

int GetManhatonDistance(int iLft, int iRgt)
{
	int iLftRow = iLft / 4;
	int iLftCol = iLft % 4;

	int iRgtRow = iRgt / 4;
	int iRgtCol = iRgt % 4;

	int d = minus(iRgtRow, iLftRow) + minus(iRgtCol, iLftCol);
//	printf("x (%d, %d) y (%d, %d) d %d\n", iLftRow, iLftCol, iRgtRow, iRgtCol, d);
	return d;
}

int EvaluateStatus(uint64_t iVal)
{
	int sum = 0;
	uint64_t iMask = 0xf;
	for(int i = 0; i < 16; i++)
	{
		int iLft = (iVal & (iMask << 60 - 4*i)) >> (60 - 4*i);
		int iPos = iLft - 1; // what it should be
	
		if(iLft != 0 && i != iPos)
		{
			//printf("val %d in %d should be pos %d\n", iLft, i, iPos);
			sum += GetManhatonDistance(i, iPos);
		}
	}
//	PrintVal(iVal);
//	printf("diff %d\n", sum);
	return sum;
}
int ans;
int limit;
bool DFS(Node& nd, int iStep)
{
	int iDiff = EvaluateStatus(nd.iFromVal);

//	printf("step %d diff %d\n", iStep, iDiff);

	if(iDiff == 0)
	{
		ans = iStep;
		return true;
	}

	if(iStep + iDiff > limit)
		return false;

	int iDirection = FindDirection(nd.iZeroIdx);
	if(iDirection & eUp && nd.iFrom != eDown)
	{
		uint64_t iVal = U(nd.iFromVal, nd.iZeroIdx);
		Node _nd;
		_nd.iFrom = eUp;
		_nd.iZeroIdx = nd.iZeroIdx - 4;
		_nd.iFromVal = iVal;

		if(DFS(_nd, iStep + 1))
			return true;

	}
	if(iDirection & eDown && nd.iFrom != eUp)
	{
		uint64_t iVal = D(nd.iFromVal, nd.iZeroIdx);

		Node _nd;
		_nd.iFrom = eDown;
		_nd.iZeroIdx = nd.iZeroIdx + 4;
		_nd.iFromVal = iVal;

		if(DFS(_nd, iStep + 1))
			return true;
	}
	if(iDirection & eLeft && nd.iFrom != eRight)
	{
		uint64_t iVal = L(nd.iFromVal, nd.iZeroIdx);

		Node _nd;
		_nd.iFrom = eLeft;
		_nd.iZeroIdx = nd.iZeroIdx - 1;
		_nd.iFromVal = iVal;

		if(DFS(_nd, iStep + 1))
			return true;
	}
	if(iDirection & eRight && nd.iFrom != eLeft)
	{
		uint64_t iVal = R(nd.iFromVal, nd.iZeroIdx);
		Node _nd;
		_nd.iFrom = eRight;
		_nd.iZeroIdx = nd.iZeroIdx + 1;
		_nd.iFromVal = iVal;

		if(DFS(_nd, iStep + 1))
			return true;
	}
	return false;
}
/*
int A_Star(priority_queue<Node> & q, set<uint64_t> & setTraveled)
{
	while(!q.empty())
	{
		auto nd = q.top();
		setTraveled.insert(nd.iFromVal);
		q.pop();
		printf("step %d\n", nd.iStep);

		int iDirection = FindDirection(nd.iZeroIdx);
		if(iDirection & eUp && nd.iFrom != eDown)
		{
			uint64_t iVal = U(nd.iFromVal, nd.iZeroIdx);
			if(setTraveled.find(iVal) != setTraveled.end())
				continue;

			int iDiff = EvaluateStatus(iVal);
			if(iDiff == 0)
				return nd.iStep + 1;

			Node _nd;
			_nd.iFrom = eUp;
			_nd.iZeroIdx = nd.iZeroIdx - 4;
			_nd.iFromVal = iVal;
			_nd.iDiff = iDiff + nd.iStep + 1;
			_nd.iStep = nd.iStep + 1;

			q.push(_nd);
		}
		if(iDirection & eDown && nd.iFrom != eUp)
		{
			uint64_t iVal = D(nd.iFromVal, nd.iZeroIdx);
			if(setTraveled.find(iVal) != setTraveled.end())
				continue;
			int iDiff = EvaluateStatus(iVal);
			if(iDiff == 0)
				return nd.iStep + 1;

			Node _nd;
			_nd.iFrom = eDown;
			_nd.iZeroIdx = nd.iZeroIdx + 4;
			_nd.iFromVal = iVal;
			_nd.iDiff = iDiff + nd.iStep + 1;
			_nd.iStep = nd.iStep + 1;

			q.push(_nd);
		}
		if(iDirection & eLeft && nd.iFrom != eRight)
		{
			uint64_t iVal = L(nd.iFromVal, nd.iZeroIdx);
			
			if(setTraveled.find(iVal) != setTraveled.end())
				continue;
			int iDiff = EvaluateStatus(iVal);
			if(iDiff == 0)
				return nd.iStep + 1;

			Node _nd;
			_nd.iFrom = eLeft;
			_nd.iZeroIdx = nd.iZeroIdx - 1;
			_nd.iFromVal = iVal;
			_nd.iDiff = iDiff + nd.iStep + 1;
			_nd.iStep = nd.iStep + 1;

			q.push(_nd);
		}
		if(iDirection & eRight && nd.iFrom != eLeft)
		{
			uint64_t iVal = R(nd.iFromVal, nd.iZeroIdx);
			if(setTraveled.find(iVal) != setTraveled.end())
				continue;
			int iDiff = EvaluateStatus(iVal);
			if(iDiff == 0)
				return nd.iStep + 1;

			Node _nd;
			_nd.iFrom = eRight;
			_nd.iZeroIdx = nd.iZeroIdx + 1;
			_nd.iFromVal = iVal;
			_nd.iDiff = iDiff + nd.iStep + 1;
			_nd.iStep = nd.iStep + 1;

			q.push(_nd);
		}


//		printf("q size %d\n", q.size());
	}
	return -1;
}
*/

uint64_t iFinal;

int main()
{
	iFinal = GetFinal();
	uint64_t iVal = 0;
	uint64_t iMask = 0xf;
	int iZeroPos = 0;
	for(int i = 0; i < 16; i++)
	{
		int n;
		scanf("%d", &n);
		iVal |= (n&iMask) << (60 - 4*i);
		if(n == 0)
			iZeroPos = i;
	}
	if(iVal == iFinal)
		printf("0\n");
	else
	{
		Node node;
		node.iZeroIdx = iZeroPos;
		node.iFromVal = iVal;

//		printf("diff %d\n", EvaluateStatus(iVal));
		
		for(limit = EvaluateStatus(iVal); limit < 100; limit ++)
		{
			if(DFS(node, 0))
			{
				printf("%d\n", ans);
				return 0;
			}
		}
		
	}
	return 0;
}

/*
int main()
{
	int T;
	scanf("%d", &T);

	iFinal = GetFinal();
	while(T--)
	{
		uint64_t iVal = 0;
		uint64_t iMask = 0xf;
		int iZeroPos = 0;
		for(int i = 0; i < 16; i++)
		{
			int n;
			scanf("%d", &n);
			iVal |= (n&iMask) << (60 - 4*i);
			if(n == 0)
				iZeroPos = i;
		}

		if(iVal == iFinal)
			printf("Yes\n");
		else if(!CanSolve(iVal))
			printf("No\n");
		else
		{
			printf("Yes\n");
		}
	}
	return 0;
}
*/



Measured memory_bytes: 3228