[백준] 11049_행렬 곱셈 순서 C++


DP를 활용하는 문제

정답제출코드


#include <iostream>
#include <vector>

#define INF 2147483647

using namespace std;

typedef pair<int, int> pii;

int N;
vector<pii> Mat;
int Sum[501], DP[501][501];

int m_min(int a, int b)
{
    if (a < b)
        return a;
    else
        return b;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    cin >> N;

    Mat.push_back({0, 0});

    for (int i = 1; i <= N; ++i)
    {
        int row, col;
        cin >> row >> col;

        Mat.push_back({row, col});
    }

   	for (int i = 1; i < N; ++i)
	{
		for (int j = 1; i+j <= N; ++j)
		{
			DP[j][i+j] = INF;
			for (int k = j; k <= i+j; ++k)
			{
				DP[j][i+j] = m_min(DP[j][i+j], DP[j][k] + DP[k+1][i+j] + Mat[j].first * Mat[k].second * Mat[i+j].second);
			}
		}

	}

	cout << DP[1][N];

    return 0;
}

시도를 해도 아이디어가 떠오르지 않아 결국 이분의 글을 참고했다.

범위가 작기 때문에 3중 for문을 사용하신 것 같다.

일단 행렬 ABC를 곱하는데 (5x3), (3x2), (2x6)을 순서대로 곱하면 최종 모습은 (5x6) 형태를 띈다.

그리고 연산의 횟수는 순서에 따라서

  • 5×3×2 + 5×2×6 (AB)C
  • 3×2×6 + 5×3×6 A(BC)

가 된다. 이 중 작은 값을 고르면 된다.

이를 구현한 부분이 아래의 코드이다.

DP[j][i+j] = m_min(DP[j][i+j], DP[j][k] + DP[k+1][i+j] + Mat[j].first * Mat[k].second * Mat[i+j].second);

그리고 참고링크의 글에 따르면

  • 가장 바깥쪽의 i는 구간 범위의 크기를 의미
  • 가운데 for문에서 j는 구간 범위의 시작지점을 의미
  • 가장 안쪽에 있는 for문에서 k는 구간 범위를 두 부분으로 나눌 때의 기준점

이다.

오답제출코드


#include <iostream>
#include <vector>

using namespace std;

typedef pair<int, int> pii;

int N;
vector<pii> Mat;
int DP[501];

int m_min(int a, int b)
{
    if (a < b)
        return a;
    else
        return b;
}

int MultiMat(pii a, pii b, pii c)
{
    int ret = a.first * a.second * b.second;
    pii mid = {a.first, b.second};

    ret += mid.first * mid.second * c.second;

    return ret;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    cin >> N;

    for (int i = 0; i < N; ++i)
    {
        int row, col;
        cin >> row >> col;

        Mat.push_back({row, col});
    }

    DP[0] = 0;
    DP[1] = 0;
    DP[2] = 0;

    pii mid = Mat[0];
    for (int i = 0; i < N-2; ++i)
    {
        DP[i+3] = DP[i+2] + m_min(MultiMat(mid, Mat[i+1], Mat[i+2]), MultiMat(Mat[i+1], Mat[i+2], mid));
        mid = {mid.first, Mat[i+2].second};
    }

    cout << DP[N];

    return 0;
}


처음엔 ABCDE가 있다고 하면 ABC부터 검사를 시작해서 (AB)C가 값이 더 작은지, A(BC)가 더 작은지 검사를 해준다.

이렇게 한 다음에 행렬의 행과 열을 새로 저장해준다.

그리고 ABC를 곱한 행렬을 F라고하면 FDE를 해줄 차례. F(DE), (FD)E 중에서 더 작은 값을 골라서 곱해준다.

이런 의도였는데 의도대로 되지 않았고, 잘못 생각한 것 같다.


© 2022.07. by Wookey_Kim

Powered by Hydejack v7.5.2