https://www.acmicpc.net/problem/2258

 

2258번: 정육점

첫째 줄에 두 정수 N(1≤N≤100,000), M(1≤M≤2,147,483,647)이 주어진다. N은 덩어리의 개수를 의미하고, M은 은혜가 필요한 고기의 양이다. 다음 N개의 줄에는 각 고기 덩어리의 무게와 가격을 나타내는

www.acmicpc.net

백준 사이트에서 풀 수 있는 정육점 문제입니다.

카테고리는 Greedy입니다.

 

19일 전에 처음 시도했던 이 문제를 중간에 과제 기간이라 하여 일주일 쉬긴 했지만 3주만에 드디어 풀었습니다.

 

풀면서 제가 막히거나 신경 쓴 부분은 다음과 같습니다.

1. 가격이 더 싼 고기를 무료로 얻을 수 있으므로 가격을 오른차순으롱 먼저 정렬하고,
무게를 빠르게 채워야 하므로 가격이 동일 한 경우 무게를 내림차순으로 정렬한다.

 

2. 가격이 더 싼 고기를 무료로 얻는다고 했지 가격이 같은 고기를 무료로 얻는다고는 안했다.

동일한 가격에 대해 누적된 가격을 계산해야 한다.

 

3. 2의 과정이 무게가 목표치보다 낮을 경우와 높을 경우에 필요한 연산이 다르다.

목표치보다 낮으면 항상 가격이 누적되고, 변동된 가격이 들어오더라도 이전 가격을 무시하면 된다.

반대로 목표치보다 높으면 가격을 더이상 누적하지 않고, 현재 최소 가격과 현재 가격만을 비교한다.

 

4. 모든 고기의 무게와 가격의 각 합인 unsigned int의 최대 값보다 작거나 같다.

때문에 조건에 부합하지 못해 -1을 출력해야 할 때 단순한 삼항연산자 등을 이용하면 <2^32 - 1>이 출력된다.
번거롭더라도 if문을 사용하거나, 출력 전에 string으로 형변환을 하는 등의 작업을 선행하자.

 

매우 간단하고, 처음에도 간단하다고 생각 했는데 3번 부분을 항상 놓치거나 잘못 작성하여 정답을 맞추지 못했다.

그리고 여태까지 문제를 풀면서 몇몇 문제는 정답 코드를 참조하기도 했는데,
이 문제는 정답률이 낮아서인지 마땅한 코드가 거의 없다.

아니, 그냥 검색 결과가 2개 정도 밖에 없다.

 

그래서 일부로 글 앞에 검색에 걸릴만한 내용을 추가하고, 하단에 코드를 작성했다.

이 문제에 막혀 참고를 하고자 하시는 분들은 다음 코드를 참고하시면 될 것 같다.

더보기
#include <iostream>
#include <vector>
#include <algorithm>

struct chunk {
	unsigned int weight;
	unsigned int price;
};

using namespace std;

int main()
{
	unsigned int length = 0, needs = 0, weight = 0, price = 0, before = -1;
	vector<struct chunk> meatlist;

	cin >> length >> needs;
	for (int i = 0; i < length; ++i)
	{
		cin >> weight >> price;
		meatlist.push_back(chunk{ weight, price });
	}

	sort(meatlist.begin(), meatlist.end(), [](const auto& a, const auto& b) { return ((a.price < b.price) || (a.price == b.price) && (a.weight > b.weight)); });

	weight = price = 0;

	for (const auto& iter : meatlist)
	{
		if (weight < needs)
		{
			if (before == iter.price)
			{
				price += iter.price;
			}
			else
			{
				before = price = iter.price;
			}
		}
		else
		{
			if ((before != iter.price) && (price >= iter.price))
			{
				before = price = iter.price;
			}
		}
		weight += iter.weight;
	}

	if (weight < needs)
	{
		cout << -1 << endl;
	}
	else
	{
		cout << price << endl;
	}

	return 0;
}

 

혹 몇가지 testcase가 필요하신 분들은 아래 파일에 있는 것을 참고하시길.

유의미 하지는 않지만 그래도 없는 것 보다는 나을 것이다.

 

정육점 input.xlsx
0.01MB

#include <iostream>
#include <vector>
#include <algorithm>

struct chunk {
	unsigned int weight;
	unsigned int price;
};

using namespace std;

int main()
{
	unsigned int length = 0, needs = 0, weight = 0, price = 0, before = -1;
	vector<struct chunk> meatlist;

	cin >> length >> needs;
	for (int i = 0; i < length; ++i)
	{
		cin >> weight >> price;
		meatlist.push_back(chunk{ weight, price });
	}

	sort(meatlist.begin(), meatlist.end(), [](const auto& a, const auto& b) { return ((a.price < b.price) || (a.price == b.price) && (a.weight > b.weight)); });
	weight = price = 0;

	for (const auto& iter : meatlist)
	{
		if (weight < needs)
		{
			if (price > iter.price)
			{
				before = price = iter.price;
			}
			else
			{
				if (before == iter.price)
				{
					price += iter.price;
				}
				else
				{
					before = price = iter.price;
				}
			}
		}
		else
		{
			if ((price > iter.price) && (before != iter.price))
			{
				before = price = iter.price;
			}
		}
		weight += iter.weight;
	}

	if (weight < needs)
	{
		cout << -1 << endl;
	}
	else
	{
		cout << price << endl;
	}

	return 0;
}

구상한 코드가 틀려 몇일 정도 다시 예외 상황이나 문제점을 고민해보도록 하겠습니다.

몇일 휴식기를 가지다 보니 집중력과 체력이 부족해 코드를 짜지는 못했습니다.

 

대신 간단한 수도 코드를 짜놓았고, 내일 이를 구현하고자 합니다.

#sort ascendent price, descendent weight
if weight < need
	if price > iter.price
    	before = price = iter.price
    else 
    	if before == iter.price
        	price += iter.price
        else 
        #price is always same or bigger than before.
        	before = price = iter.price
else
	if price > iter.price && before != iter.price
    	before = price = iter.price
weight += iter.price

https://www.acmicpc.net/problem/2258

 

2258번: 정육점

첫째 줄에 두 정수 N(1≤N≤100,000), M(1≤M≤2,147,483,647)이 주어진다. N은 덩어리의 개수를 의미하고, M은 은혜가 필요한 고기의 양이다. 다음 N개의 줄에는 각 고기 덩어리의 무게와 가격을 나타내는

www.acmicpc.net

사실 그저께부터 풀어보려 했으나 번번히 실패하는 문제입니다.

 

정육점에는 여러 고기 덩어리가 있고, 각 고기 덩어리는 무게와 가격이 천차만별입니다.

은혜는 본인이 필요로 하는 양의 고기를 사면 됩니다.

단, 가격이 더 싸다면 필요한 양보다 더 많은 고기를 살 수도 있습니다.

또한 자신이 구매한 고기보다 싼 고기들은 추가 지불 없이 얼마든지 구매할 수 있습니다.

 

이 때 은혜가 원하는 양의 고기를 구매하기 위한 최소 비용을 계산해야 합니다.

 

여태까지 제가 구현한 방식은 다음과 같습니다.

1. 무게합이 필요한 무게보다 작은지 비교한다.

2. 1의 결과가 참일 경우, 최소 비용의 무게와 현재 무게가 동일한지 비교한다.

3. 2의 결과가 참일 경우, 최소 비용과 현재 비용을 비교하여 더 작은 것을 저장한다.

4. 2의 결과가 거짓일 경우, 최소 비용과 그 무게를 현재 비용과 무게로 저장한다.

5. 1의 결과가 거짓일 경우, 최소 비용을 현재 무게와 비교하여 더 작은 것을 저장한다.

6. 무게합이 요구치보다 작을 경우 -1을, 클 경우 최소 비용을 출력한다.

 

하지만 9%를 넘어가는 정도에서 정답을 맞추지 못하고 실패해버립니다.

 

오늘은 이쯤에서 멈추고 내일 다시 풀어보겠습니다.

https://www.acmicpc.net/problem/14731

 

14731번: 謎紛芥索紀 (Large)

성민이는 이번 학기에 미적분학 과목을 수강하고 있다. 다항함수의 미분 단원 과제를 하던 도중 미분을 하기가 귀찮아진 성민이는 미분하려는 함수 f(x)가 주어지면, 미분 된 함수 f’(x)를 자동��

www.acmicpc.net

100K개의 다항식의 도함수 f'(x)에 대해 x = 2일 때의 값을 1B + 7로 나눈 나머지를 구하는 문제이다.

매우 간단하지만, 연산 시간을 맞추는 것에 어려움이 많았다.

 

우선 차수가 1B까지 나올 수 있는 지수 연산이 문제였다.

처음에는 greedy하게 하나하나 해봤지만, 역시나 시간이 부족했다.

 

그 다음에는 2배수로 나눠서 연산을 해보려 했으나,

반대로 입력받은 지수 값을 2진수로 계산하는 것이 너무 오래 걸렸습니다.

 

결국 답은 지수를 10진수 단위로 끊는 것이었습니다.

10^n 단위로 미리 연산을 해두고, 이를 이용한 pow 함수를 따로 제작하여 이를 사용하였습니다.

 

하지만 이후에도 수차례 통과하지 못했습니다.

여러 차례 수정을 하다가 깨달은 점은, 지수 부분만 unsigned long long으로 하였으나

sizeof(unsigned int) 크기를 넘어갈 수 있다는 점이었습니다.

 

그래서 size를 제외한 모든 값을 unsigned long long으로 선언을 해주고 나서야 비로소 통과 할 수 있었습니다.

더보기
#include <iostream>
#include <vector>
#include <array>
#include <algorithm>

using namespace std;

array<unsigned long long, 10> decpow;

unsigned long long GetPow(unsigned int dec)
{
    if (dec == 0)
    {
        return 1;
    }
    int rest = 0, index = 0;
    unsigned long long output = 1;

    while (dec > 0)
    {
        rest = dec % 10;
        dec /= 10;
        for (int i = 0; i < rest; ++i)
        {
            output *= decpow[index];
            output %= 1000000007;
        }
        ++index;
    }

    return output;
}

int main()
{
    unsigned int size = 0;
    unsigned long long output = 0, a = 1, b = 0, value = 2, exp = 1;
    vector<pair<unsigned long long, unsigned long long>> input;
    decpow[0] = 2;
    for (int i = 1; i < 9; ++i)
    {
        exp = 1;
        for (int j = 0; j < 10; ++j)
        {
            exp *= value;
            exp %= 1000000007;
        }
        value = exp;
        decpow[i] = value;
    }

    cin >> size;
    for (int i = 0; i < size; ++i)
    {
        cin >> a >> b;
        if (b > 0)
        {
            input.push_back(make_pair(a * b % 1000000007, b - 1));
        }
    }

    sort(input.begin(), input.end(), [](auto& a, auto& b)
        {
            return a.second < b.second;
        });

    for (const auto& iter : input)
    {
        output += iter.first * GetPow(iter.second);
        output %= 1000000007;
    }

    cout << output << endl;
    return 0;
}

https://www.acmicpc.net/problem/14754

 

14754번: Pizza Boxes

Your program is to read from standard input. The input contains two integers, n and m (1 ≤ n, m ≤ 1,000), the number of rows and columns in the grid, respectively. Each of the following n lines contain m integers, the number of pizza boxes (heights) in

www.acmicpc.net

피자 박스가 N * M으로 쌓여 있습니다.

이 때 정면도와 측면도가 바뀌지 않도록 피자 박스를 제거 할 때, 최대 제거되는 피자 박스의 수를 구하는 문제입니다.

 

제가 선택한 방식은 다음과 같습니다.

0. 입력 받을 때 모든 상자 박스 높이의 합을 더합니다.

1. row 단위에서 최대 높이인 index를 vector에 저장합니다.

2. column 단위에서 최대 높이인 index를 vector에 저장합니다.

3. 단, 1과 2의 과정에서 vector 안에 동일한 index가 존재하면 저장하지 않습니다.

4. 저장된 indices의 상자 높이 만큼 연산된 상자 높이의 합에서 감산합니다.

 

처음 시도에는 오답을 받았습니다.

무엇이 문제인지 살펴보니, 상자 높이가 1B까지 입력 받고, 상자 뭉치 갯수는 1M개 만큼 입력을 받을 수 있었습니다.

입력값이 많을 경우, 상자 높이의 합이 int의 범위를 넘어설 수 있습니다.

그래서 상자 높이의 합을 int에서 unsigned long long으로 변경하였고, 정답을 맞출 수 있었습니다.

 

문제를 다 풀고 상자 순회를 한번에 해결할 수 있나 고민을 해보았습니다.

하지만 column과 row 단위에서 비교를 해 최대값을 뽑아야 하는 만큼,

둘 중 하나는 그 크기 만큼 최대값을 동시에 관리해야 했습니다.

이를 판단하는 것보다 차라리 순회를 두번 하는 것이 덜 복잡할 것 같습니다.

 

더보기
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
    int column = 0, row = 0, input = 0;
    unsigned long long output = 0;
    vector<vector<int>> tower;
    vector<vector<int>> LiveIndex;
    cin >> column >> row;
    for (int i = 0; i < column; ++i)
    {
        tower.push_back(vector<int>(row));
        for (int j = 0; j < row; ++j)
        {
            cin >> input;
            tower[i][j] = input;
            output += input;
        }
    }

    for (int i = 0; i < column; ++i)
    {
        int max = 0, cIndex = 0, rIndex = 0;
        for (int j = 0; j < row; ++j)
        {
            if (max < tower[i][j])
            {
                cIndex = i;
                rIndex = j;
                max = tower[i][j];
            }
        }
        auto temp = vector<int>{ cIndex, rIndex };
        if (find(LiveIndex.begin(), LiveIndex.end(), temp) == LiveIndex.end())
        {
            LiveIndex.push_back(temp);
        }
    }
    for (int i = 0; i < row; ++i)
    {
        int max = 0, cIndex = 0, rIndex = 0;
        for (int j = 0; j < column; ++j)
        {
            if (max < tower[j][i])
            {
                cIndex = j;
                rIndex = i;
                max = tower[j][i];
            }
        }
        auto temp = vector<int>{ cIndex, rIndex };
        if (find(LiveIndex.begin(), LiveIndex.end(), temp) == LiveIndex.end())
        {
            LiveIndex.push_back(temp);
        }
    }
    for (const auto& iter : LiveIndex)
    {
        output -= tower[iter[0]][iter[1]];
    }

    cout << output << endl;
    return 0;
}

https://www.acmicpc.net/problem/13333

 

13333번: Q-인덱스

문제 ICPC 대학의 모든 박사과정 학생은 자신이 발표한 논문과 그 논문들의 인용횟수를 고려한 학위 취득 조건을 만족해야 한다. 이를 위해, ICPC 대학은 q-인덱스라는 값을 정의했다. 이 인덱스는

www.acmicpc.net

입력된 n개의 논문들의 인용 횟수를 받고 k | N(q(i) ≥ k) k, N(q(i) k) k (0 i n)인 k를 구하는 문제입니다.

 

제가 처음 선택한 풀이법은 다음과 같습니다.

1. 입력받은 리스트를 오름차순으로 정렬한다.

2. index를 순회하면서 q(i) ≥ i && q(i) ≤ n - i인지 확인을 한다.

3. 2의 조건이 만족할 경우, i 값을 저장한다.

4. 순회를 모두 마친 후 최후의 저장된 i값을 출력한다.

 

하지만 이 문제에는 몇가지 큰 문제점이 있었습니다.

1) Q-index 값이 입력된 논문의 인용 횟수 내로 제한된다.

2) 논문 인용 횟수가 논문 갯수보다 월등히 높으면 Q-index가 0으로 연산된다.

 

하지만 적은 것과 다르게 이를 알아내는데에는 6차례의 추가 시도가 필요했습니다.

 

그리고 결국 수정한 방안은 다음과 같습니다.

1. 입력받은 리스트를 오름차순으로 정렬한다.

2. 임의의 qindex를 Max(q(i))까지 순회한다.

3. 2의 순회에서 qindex보다 크거나 같은 q(i)를 갖는 index를 구한다.

4. index ≤ qindex && n - index ≥ qindex인 qindex를 저장한다.

5. 순회를 마치고 최종적으로 저장된 qindex를 출력한다.

 

문제 성격상 내림차순으로 정렬하여 qindex를 내림차순으로 순회하면 조금 더 빠르게 풀 수 있겠지만, 

이대로도 충분히 해결이 되기에 따로 수정을 하지는 않았습니다.

더보기
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
    int size = 0, input = 0, output = 0, max = 0;
    vector<int> report;
    cin >> size;
    for (int i = 0; i < size; ++i)
    {
        cin >> input;
        report.push_back(input);
    }

    sort(report.begin(), report.end());
    max = report[size - 1];

    for (int qIndex = 0; qIndex < max; ++qIndex)
    {
        int index = 0;
        while (report[index] < qIndex)
        {
            ++index;
        }
        if ((index <= qIndex) && (size - index >= qIndex))
        {
            output = qIndex;
        }
    }

    cout << output << endl;

    return 0;
}

 

https://www.acmicpc.net/problem/10253

 

10253번: 헨리

문제 이제 10 살이 된 헨리(Henry)는 수학에 소질이 있다. 수학선생님인 아메스(Ahmes)는 오늘 헨리에게 분수에 대해 가르쳐줬고, 헨리는 분수를 이리저리 계산해보는 것이 너무 재미있었다. 그러던

www.acmicpc.net

a / b 분수가 입력 되었을 때 x(i) = {x | a/b >= 1 / x 중 가장 큰 x}일 때 가장 마지막 x(i)를 출력하는 문제입니다.

 

처음에는 gcd와 lcm으로 연산을 하였으나, 시간이 너무 오래 걸렸습니다.

 

그 다음에 연산한 것은 a와 b의 각 차수 별 항에서 규칙성을 찾아서 적용하는 것입니다

min(i) = b(i) / a(i) + 1일 때, a(i + 1) = a(i) * min(i) - b(i), b(i + 1) = b(i) * min(i)를 적용하였습니다.

하지만 이 역시 시간 초과가 걸렸습니다.

 

솔직히 이보다 더 빠르게 만들 자신이 없었습니다.

입력을 c스타일 입력으로 바꾸기도 했으나, 여전히 문제가 해결되지 않았습니다.

 

그러다가 정답을 찾아 보는데, 어이 없는 문제점이 두개가 있었습니다.

 

하나는 반복문을 do-while이 아니라 그냥 while문으로 하는 것.

이렇게 되면 처음부터 분자가 1일 때는 자기 자신이 출력이 되는데 왜 성립하는지 몰랐습니다.

하지만 곧, 제가 문제를 잘못 읽었다는 것을 깨달았습니다.

이 문제는 자기 자신이 답이 될 수 있었습니다.

이 부분이 조금 뼈아팠습니다.

 

다른 하나의 문제는 연산이 다 끝나면 항상 gcd로 나누어 주는 것.

서로소를 곱하고 더하는데 최대공약수가 1보다 큰 경우가 발생을 하는가 싶었습니다.

이를 확인하기 위해 값을 최대로 올려보았더니, 곧바로 확인이 가능했습니다.

1에 근접하고 분모가 매우 큰 경우에는 종종 최대공약수가 1보다 큰 경우가 발생하기도 하였습니다.

 

문제의 해결법은 곧잘 찾아내지만, 아직은 세세하게 런타임을 줄이지는 못하는 것 같습니다.

매일 한문제씩 풀어나가면서 이런 실수를 최대한 줄이고, 문제 푸는 속도도 키우도록 하겠습니다.

더보기
#include <iostream>

using namespace std;

int GCD(const int& a, const int& b)
{
    return (b == 0) ? a : GCD(b, a % b);
}

int main()
{
    int testcase = 0, a = 0, b = 0, min = 0, gcd = 0;
    
    cin >> testcase;

    while (testcase-- > 0)
    {
        cin >> a >> b;        
        while (a != 1)
        {
            min = (b + a - 1) / a;
            a = a * min - b;
            b *= min;
            gcd = GCD(a, b);
            a /= gcd;
            b /= gcd;
        } 
        cout << b << endl;
    }

    return 0;
}

괄호가 지워진 길이 50 이하의 덧셈과 뺄셈만 존재하는 식의 최소값을 구하는 문제입니다.

숫자는 99999 이하이며, 0부터 시작 할 수 있습니다.

식은 숫자로 시작하고 숫자로 끝나며, 연산자가 두개 이상 연속으로 입력되지 않습니다.

 

최소가 나오는 방법은 간단합니다.

- 연산자를 기점으로 괄호를 쳐서 그 안의 연산을 우선적으로 해주면 됩니다.

 

구현 방법으로는 입력되는 식을 -로 split 해주고, 각 substring을 다시 +로 split한 뒤 연산을 해줍니다.

그리고 그 결과물을 다시 - 연산을 해주면 됩니다.

 

이 간단한 문제를 저는 처음 시작하는 substring에도

+ 연산이 포함될 수 있다는 것을 간과하여 3번이나 실패를 했습니다.

정답율도 50% 언저리였는데 참 슬펐습니다.

 

더보기
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>

using namespace std;

const int StringToInt(const string& str)
{
    int ret = 0;
    for (unsigned int i = 0; i < str.size(); ++i)
    {
        ret *= 10;
        ret += str[i] - '0';
    }
    return ret;
}

const long long StringToList(const string& list)
{
    long long ret = 0;
    string temp;
    vector<string> pParse;
    stringstream ss(list);
    
    while (getline(ss, temp, '+'))
    {
        pParse.push_back(temp);
    }
    for (const auto& iter : pParse)
    {
        ret += StringToInt(iter);
    }
    return ret;
}

int main()
{
    long long ret = 0;
    string input, temp;
    vector<string> mParse;
    vector<int> mParseNum;

    cin >> input;
    stringstream ss(input);
    while (getline(ss, temp, '-'))
    {
        mParse.push_back(temp);
    }
    ret = StringToList(mParse[0]);
    for (unsigned int i = 1; i < mParse.size(); ++i)
    {
        ret -= StringToList(mParse[i]);
    }
    cout << ret << endl;

    return 0;
}

https://www.acmicpc.net/problem/6064

 

6064번: 카잉 달력

문제 최근에 ICPC 탐사대는 남아메리카의 잉카 제국이 놀라운 문명을 지닌 카잉 제국을 토대로 하여 세워졌다는 사실을 발견했다. 카잉 제국의 백성들은 특이한 달력을 사용한 것으로 알려져 있�

www.acmicpc.net

문제가 워낙 쉬워 보여 하나 더 풀어보았습니다.

S = {<x:y> | x = S % M, y = S % N}라고 했을 때 S를 구하라는 문제입니다.

간단히 두 Modular 연산 결과를 보고 원래 수를 구하라는 문제인데,

간단하게 하나씩 더해보았는데 정답을 맞출 수 있었습니다. 

 

단, x와 y중 하나를 기준으로 M과 N의 최소공배수 이상 더했음에도 답이 나오지 않으면

나올 수 없는 결과 값이므로 -1을 출력합니다.

사실 귀찮아서 최소공배수가 아니라 M * N까지 연산을 하였는데 아슬아슬하게 통과할 수 있었습니다.

 

이 문제를 언제 왜 못풀었나 살펴보니 4년 전이었습니다.

아마 대충 도전 했다가 놀았거나,

다국어로 되어 있는 걸로 보아 영어로 적힌 것을 해석하지 못하고 포기했던 것 같습니다.

 

더보기
#include <iostream>

using namespace std;

int main()
{
    int testcase = 0, M = 0, N = 0, x = 0, y = 0;
    cin >> testcase;
    while (testcase-- > 0)
    {
        int sum = 0;
        cin >> M >> N >> x >> y;
        while ((x != y) && (sum < (M * N)))
        {
            if (x > y)
            {
                y += N;
                sum += N;
            }
            else
            {
                x += M;
            }
        }
        if (sum >= (M * N))
        {
            cout << -1 << endl;
        }
        else
        {
            cout << x << endl;
        }
    }
}

 

+ Recent posts