Post

[ STUDY/코딩문제 ] 2022. 8. 20. 21:55

유기농 배추 성공


시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율
1 초 512 MB 118457 46129 31222 36.963%

 

문제

차세대 영농인 한나는 강원도 고랭지에서 유기농 배추를 재배하기로 하였다. 농약을 쓰지 않고 배추를 재배하려면 배추를 해충으로부터 보호하는 것이 중요하기 때문에, 한나는 해충 방지에 효과적인 배추흰지렁이를 구입하기로 결심한다. 이 지렁이는 배추근처에 서식하며 해충을 잡아 먹음으로써 배추를 보호한다. 특히, 어떤 배추에 배추흰지렁이가 한 마리라도 살고 있으면 이 지렁이는 인접한 다른 배추로 이동할 수 있어, 그 배추들 역시 해충으로부터 보호받을 수 있다. 한 배추의 상하좌우 네 방향에 다른 배추가 위치한 경우에 서로 인접해있는 것이다.

한나가 배추를 재배하는 땅은 고르지 못해서 배추를 군데군데 심어 놓았다. 배추들이 모여있는 곳에는 배추흰지렁이가 한 마리만 있으면 되므로 서로 인접해있는 배추들이 몇 군데에 퍼져있는지 조사하면 총 몇 마리의 지렁이가 필요한지 알 수 있다. 예를 들어 배추밭이 아래와 같이 구성되어 있으면 최소 5마리의 배추흰지렁이가 필요하다. 0은 배추가 심어져 있지 않은 땅이고, 1은 배추가 심어져 있는 땅을 나타낸다.

1 1 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0
0 0 1 1 0 0 0 1 1 1
0 0 0 0 1 0 0 1 1 1

입력

입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트 케이스에 대해 첫째 줄에는 배추를 심은 배추밭의 가로길이 M(1 ≤ M ≤ 50)과 세로길이 N(1 ≤ N ≤ 50), 그리고 배추가 심어져 있는 위치의 개수 K(1 ≤ K ≤ 2500)이 주어진다. 그 다음 K줄에는 배추의 위치 X(0 ≤ X ≤ M-1), Y(0 ≤ Y ≤ N-1)가 주어진다. 두 배추의 위치가 같은 경우는 없다.

출력

각 테스트 케이스에 대해 필요한 최소의 배추흰지렁이 마리 수를 출력한다.


풀이

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;


int map[52][52] = {0};
int check_map[52][52] = {0};
int num = 1;


bool Ischange = false;


void DFS(int nowI,int nowJ)
{
	if (map[nowI][nowJ] == 0 || check_map[nowI - 1][nowJ - 1] > 1) //아예 배추가 없거나 or 확인한적 있거나
		return;
	else
	{
		check_map[nowI - 1][nowJ - 1] = num;
		map[nowI][nowJ] = 0;
	

		Ischange = true;
		if (map[nowI - 1][nowJ] == 1) //상
			if(check_map[nowI - 2][nowJ-1] < 1)//한번도 check안한 곳
				DFS(nowI - 1, nowJ);
		if(map[nowI + 1][nowJ] == 1)//하
			if (check_map[nowI][nowJ-1] < 1)//한번도 check안한 곳
				DFS(nowI + 1, nowJ);
		if (map[nowI ][nowJ -1] == 1) //좌
			if (check_map[nowI -1][nowJ - 2] < 1)//한번도 check안한 곳
				DFS(nowI , nowJ - 1);
		if (map[nowI ][nowJ +1] == 1)//우
			if (check_map[nowI-1][nowJ] < 1)//한번도 check안한 곳
				DFS(nowI , nowJ+1);
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	int T, M, N, K;
	int x, y;

	
	cin >> T;
	

	for (int l = 0; l < T; l++) {

		//입력부
		cin >> M >> N >> K;
		for (int i = 0; i < K; i++) 
		{
			cin >> x >> y;

			map[x+1][y+1] = 1;

		}

		//연산부
		for (int i = 1; i <= M; i++)
		{
			for (int j = 1; j <= N; j++)
			{
				DFS(i, j);
				if (Ischange == true)
				{
					num++;
					Ischange = false;
				}
			}
		}


		//출력부
		num--;
		cout << num << '\n';


		memset(map, 0, sizeof(map));
		memset(check_map, 0, sizeof(check_map));
		
		num = 1;
		Ischange = false;
	}
	

	
	
}

memset함수를 쓸때는 #include<cstring> 꼭 해줘야합니다.

아니면 컴파일에러가 뜨거든용. ╯︿╰

 

바로앞에 풀었던 [2667]단지번호붙이기 와 매우 유사한 문제. 그냥 똑같습니다.! 이것도 DFS로 풀었습니다.

 

 

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

 

1012번: 유기농 배추

차세대 영농인 한나는 강원도 고랭지에서 유기농 배추를 재배하기로 하였다. 농약을 쓰지 않고 배추를 재배하려면 배추를 해충으로부터 보호하는 것이 중요하기 때문에, 한나는 해충 방지에 

www.acmicpc.net

 

Post

[ STUDY/코딩문제 ] 2022. 8. 20. 12:10

단지번호붙이기 성공


시간 제한 메모리 제한 제출 제출 맞힌 사람 정답 비율
1 초 128 MB 126328 54327 34339 40.847%

 

문제

<그림 1>과 같이 정사각형 모양의 지도가 있다. 1은 집이 있는 곳을, 0은 집이 없는 곳을 나타낸다. 철수는 이 지도를 가지고 연결된 집의 모임인 단지를 정의하고, 단지에 번호를 붙이려 한다. 여기서 연결되었다는 것은 어떤 집이 좌우, 혹은 아래위로 다른 집이 있는 경우를 말한다. 대각선상에 집이 있는 경우는 연결된 것이 아니다. <그림 2>는 <그림 1>을 단지별로 번호를 붙인 것이다. 지도를 입력하여 단지수를 출력하고, 각 단지에 속하는 집의 수를 오름차순으로 정렬하여 출력하는 프로그램을 작성하시오.

입력

첫 번째 줄에는 지도의 크기 N(정사각형이므로 가로와 세로의 크기는 같으며 5≤N≤25)이 입력되고, 그 다음 N줄에는 각각 N개의 자료(0혹은 1)가 입력된다.

출력

첫 번째 줄에는 총 단지수를 출력하시오. 그리고 각 단지내 집의 수를 오름차순으로 정렬하여 한 줄에 하나씩 출력하시오.

 


풀이

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;


int map[26][26] = {0};
int check_map[25][25] = {0};
int num = 1;


bool Ischange = false;
vector<int> res;
int home[626] = { 0 }; 

void DFS(int nowI,int nowJ)
{
	if (map[nowI][nowJ] == 0 || check_map[nowI - 1][nowJ - 1] > 1) //아예 집이없거나 or 확인한적 있거나
		return;
	else
	{
		check_map[nowI - 1][nowJ - 1] = num;
		map[nowI][nowJ] = 0;
		home[num-1]++;
	

		Ischange = true;
		if (map[nowI - 1][nowJ] == 1) //상
			if(check_map[nowI - 2][nowJ-1] < 1)//한번도 check안한 곳
				DFS(nowI - 1, nowJ);
		if(map[nowI + 1][nowJ] == 1)//하
			if (check_map[nowI][nowJ-1] < 1)//한번도 check안한 곳
				DFS(nowI + 1, nowJ);
		if (map[nowI ][nowJ -1] == 1) //좌
			if (check_map[nowI -1][nowJ - 2] < 1)//한번도 check안한 곳
				DFS(nowI , nowJ - 1);
		if (map[nowI ][nowJ +1] == 1)//우
			if (check_map[nowI-1][nowJ] < 1)//한번도 check안한 곳
				DFS(nowI , nowJ+1);
	}
}

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


	int N;
	string input = "0000000";
	cin >> N;

	for (int i = 1; i <= N; i++)
	{
		cin >> input;
		for (int j = 0; j <N; j++)
		{
			map[i][j+1] = input[j] - '0';
			}
	}
	
	for (int i = 1; i <= N; i++)
	{
		for (int j = 1; j <= N; j++)
		{
			DFS(i, j);
			if (Ischange == true)
			{
				num++; 
				Ischange = false;
			}
		}
	}

	
	num--;

	cout << num<<'\n';
	for (int i = 0; i < num; i++)
		res.push_back(home[i]);

	sort(res.begin(), res.end());

	for (int i = 0; i < res.size(); i++)
		cout << res[i]<<'\n';

	
}

 

움..일단 DFS로 문제를 풀었고, 배열의 크기때문에 애를 먹었습니다.

000000000
011111110
011111110
011111110
011111110
011111110
011111110
011111110
000000000

이렇게 입력받은값들을 0이라는 벽안에 가둬둘려고했고, 이러면 따로 상하좌우 1이 있는지 검사할때 범위밖으로 나갈일이 없다고 생각을하고 map[26][26]을 했는데.. 제가 생각해놓은걸 구현할려면 [27][27] 이렇게 해야했네요 ㅎㅎㅎ;;

 

map은[26][26]으로하고, 여길 내가 전에 check했는지를 알기위해 check_map은 또 크기를[25][25]로 했더니 for문 돌릴때도 그렇고 DFS에서 if문으로 체크할때도 꽤나 애먹었습니다. 

 

그리고 입력받을때 연속된 숫자들(띄어쓰기없이)을 받기때문에 그냥 int형으로 받지않고 문자열로 받되 그 문자열의 문자 하나하나에 '0'을 빼주어 숫자로 배열에 넣어주었습니다.

 

나름 머리쓴다고 배열의 크기를 생각하다가 제꾀에 제가 넘어간듯한 느낌이었습니다. 그래도 나름 머리에 생각한걸 그대로 표현한것같아서 뿌듯하네요! :D

 

 

 

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

 

2667번: 단지번호붙이기

<그림 1>과 같이 정사각형 모양의 지도가 있다. 1은 집이 있는 곳을, 0은 집이 없는 곳을 나타낸다. 철수는 이 지도를 가지고 연결된 집의 모임인 단지를 정의하고, 단지에 번호를 붙이려 한다. 여

www.acmicpc.net

 

 

 

 

 

Post

[ STUDY/코딩문제 ] 2022. 8. 14. 23:34

행성 연결 성공


 

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율
1 초 256 MB 3807 1734 1236 42.591%

문제

홍익 제국의 중심은 행성 T이다. 제국의 황제 윤석이는 행성 T에서 제국을 효과적으로 통치하기 위해서, N개의 행성 간에 플로우를 설치하려고 한다.

두 행성 간에 플로우를 설치하면 제국의 함선과 무역선들은 한 행성에서 다른 행성으로 무시할 수 있을 만큼 짧은 시간만에 이동할 수 있다. 하지만, 치안을 유지하기 위해서 플로우 내에 제국군을 주둔시켜야 한다.

모든 행성 간에 플로우를 설치하고 플로우 내에 제국군을 주둔하면, 제국의 제정이 악화되기 때문에 황제 윤석이는 제국의 모든 행성을 연결하면서 플로우 관리 비용을 최소한으로 하려 한다.

N개의 행성은 정수 1,…,N으로 표시하고, 행성 i와 행성 j사이의 플로우 관리비용은 Cij이며, i = j인 경우 항상 0이다.

제국의 참모인 당신은 제국의 황제 윤석이를 도와 제국 내 모든 행성을 연결하고, 그 유지비용을 최소화하자. 이때 플로우의 설치비용은 무시하기로 한다.

입력

입력으로 첫 줄에 행성의 수 N (1 ≤ N ≤ 1000)이 주어진다.

두 번째 줄부터 N+1줄까지 각 행성간의 플로우 관리 비용이 N x N 행렬 (Cij), (1 ≤ i, j ≤ N, 1 ≤ Cij ≤ 100,000,000, Cij = Cji, Cii = 0) 로 주어진다.

출력

 

모든 행성을 연결했을 때, 최소 플로우의 관리비용을 출력한다.

 


풀이

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

int V, E;
vector<pair<long long, pair<int, int>>> edge;
int parents[1000];
int edgenum = 0;

int FindRoot(int x)
{
	if (x == parents[x])
		return x;
	return parents[x] = FindRoot(parents[x]);
}

void Merge(int x, int y)
{
	x = FindRoot(x);
	y = FindRoot(y);

	if (x == y)
		return;
	if (y > x)
		parents[y] = x;
	else
		parents[x] = y;
}





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


	for (int i = 0; i < 1000; i++)
	{
		parents[i] = i;
	}

	int N;
	long long res = 0;
	cin >> N;

	long long input;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++) {
			cin >> input;
			if ((i < j) && (i != j)) {
				edge.push_back({ input,{i,j} });
			}
		}

	}


	sort(edge.begin(), edge.end());

	for (int i = 0; i < edge.size(); i++)
	{
		if (FindRoot(edge[i].second.first) == FindRoot(edge[i].second.second))
			continue;

		Merge(edge[i].second.first, edge[i].second.second);
		res += edge[i].first; 
		edgenum++;

		if (edgenum == N - 1)
			break;

	}
	cout << res<<'\n';
}

이 문제는 같은종류의 문제였던 최소스패닝트리의 전력난과 최소스패닝트리의 문제풀이만을 생각하고있다가 통수맞았습니다! 사실 통수가 아니라 제 지식과 응용의 문제이기도 하겠지만 이전까지 pair<int,pair<int,int>>를 잘 해오다가 이번 문제에서는 똑같이하면 안된다는것을 알았습니다.

Vector<pair<long long,pair<int,int>>>했을때 (좌) , pair<long long,pair<int,int>> 했을때(우)

위 사진은 각각 다르게 자료형을 가졌을때, sort하고 난 다음의 모습입니다.

vector<pair<long long,pair<int,int>>>로 하였을때는 sort가 cost의 오름차순으로 잘 정렬되어있지만, pair<long long,pair<int,int>> 를 했을 경우에는 중간부터 정렬이 어긋난것을 알 수가 있습니다.

 

이 원인은 알고보니 sort함수때문에 일어났는데요..제가 바보같이 pair<long long,pair<int,int>>를 했을 당시 sort를 (edge, edge+N)으로 두는 어마어마한 실수를 하였습니다. 분명 마지막 항목은 N이 아니고 그 배일텐데.. 제가  정말 앞에 푼 문제들만 생각하고 심각한 오류를 범했죠..

 

다행히 Vector<pair<long long,pair<int,int>>>를 했을때는 sort를 sort(edge.bigin(), edge.end())로 두었기에 제가 신경쓰지 않더라도 알아서 잘 정렬을 해주었습니다.

 

이 글을 쓰면서 다시 한번 쭉 검토해보니 통수가 아니라 정말 바보같은 실수네요! 오랜만에 문제를 풀어보았더니 이런 실수나 하고..부끄럽습니다! 열심히 이제부터 다시 공부해야겠네요 :D

 

끝만 잘 생각한다면 그냥 크루스칼 알고리즘 그대로 써도 될 정도의 최소스패닝트리 기본문제였습니다.

 

 

 

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

 

16398번: 행성 연결

홍익 제국의 중심은 행성 T이다. 제국의 황제 윤석이는 행성 T에서 제국을 효과적으로 통치하기 위해서, N개의 행성 간에 플로우를 설치하려고 한다. 두 행성 간에 플로우를 설치하면 제국의 함

www.acmicpc.net

 

Post

[ STUDY/코딩문제 ] 2022. 5. 29. 17:50

구간 합 구하기 성공


 

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율
2 초 256 MB 64661 13198 6829 23.790%

 

문제

어떤 N개의 수가 주어져 있다. 그런데 중간에 수의 변경이 빈번히 일어나고 그 중간에 어떤 부분의 합을 구하려 한다. 만약에 1,2,3,4,5 라는 수가 있고, 3번째 수를 6으로 바꾸고 2번째부터 5번째까지 합을 구하라고 한다면 17을 출력하면 되는 것이다. 그리고 그 상태에서 다섯 번째 수를 2로 바꾸고 3번째부터 5번째까지 합을 구하라고 한다면 12가 될 것이다.

 

입력

첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000,000)과 M(1 ≤ M ≤ 10,000), K(1 ≤ K ≤ 10,000) 가 주어진다. M은 수의 변경이 일어나는 횟수이고, K는 구간의 합을 구하는 횟수이다. 그리고 둘째 줄부터 N+1번째 줄까지 N개의 수가 주어진다. 그리고 N+2번째 줄부터 N+M+K+1번째 줄까지 세 개의 정수 a, b, c가 주어지는데, a가 1인 경우 b(1 ≤ b ≤ N)번째 수를 c로 바꾸고 a가 2인 경우에는 b(1 ≤ b ≤ N)번째 수부터 c(b ≤ c ≤ N)번째 수까지의 합을 구하여 출력하면 된다.

입력으로 주어지는 모든 수는 -263보다 크거나 같고, 263-1보다 작거나 같은 정수이다.

 

출력

첫째 줄부터 K줄에 걸쳐 구한 구간의 합을 출력한다. 단, 정답은 -263보다 크거나 같고, 263-1보다 작거나 같은 정수이다.

 


풀이

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

vector<long long> numbers(9000001);
vector<long long> tree(9000001);


long long InitTree(vector<long long>& numbers, vector<long long>& tree, int node, int start, int end)
{

	if (start == end)
		return tree[node] = numbers[start];

	int mid = (start + end) / 2;

	return tree[node] = InitTree(numbers, tree, node * 2, start, mid) +
		InitTree(numbers, tree, node * 2 + 1, mid + 1, end);
}


long long GetPrefixSum(vector<long long>& tree, int node, int start, int end, int left, int right) {
	if (left > end || right < start)
		return 0;
	if (left <= start && end <= right)
		return tree[node];

	int mid = (start + end) / 2;
	return GetPrefixSum(tree, node * 2, start, mid, left, right) +
		GetPrefixSum(tree, node * 2 + 1, mid + 1, end, left, right);
}

void UpdateTree(vector<long long>& tree, int node, int start, int end,
	int index, long long difference) {
	if (!(start <= index && index <= end))
		return;

	tree[node] += difference;

	if (start != end) {
		long long mid = (start + end) / 2;
		UpdateTree(tree, node * 2, start, mid, index, difference);
		UpdateTree(tree, node * 2 + 1, mid + 1, end, index, difference);
	}
}


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

	long long N, M, K;

	cin >> N >> M >> K;


	for (int i = 1; i <= N; i++)
	{
		cin >> numbers[i];
		
	}

	InitTree(numbers, tree, 1, 1, N);
	int a;
	long long  b, c;
	for (int j = 0; j < M + K; j++)
	{
		cin >> a >> b >> c;
		if (a == 1)
		{
			UpdateTree(tree, 1, 1, N, b, c - numbers[b]);
			numbers[b] = c;
		}
		else if (a == 2)
		{
			cout<<GetPrefixSum(tree, 1, 1, N, b, c)<<'\n';
		}
	}


}

노가다의 흔적

이 문제는 처음에 세그먼트 트리를 그냥 바로 쓰면 되는줄 알고 단순하게 생각하고 통수 7번 맞은 문제입니다. 심지어 맞추고 나서도 통수 2번 더 맞았습니다.

 

처음 메모리초과는 입력받는 정수와 벡터의 자료형을 int로 하여서 많이 떳습니다. => 결국 정답은 long long

그 다음에는 여러군데 많이 고쳐줘서 맞았습니다.가 떳지만 너무 많이 고쳤기에 어느 부분때문에 맞은건지를 잘 몰라서

정답을 맞췄음에도 불구하고 정답률을 낮추기위해 한 몸 희생해서 제출을 더해보았습니다.

 

런타임에러는 벡터의 크기를 작게 잡았기때문에 떳습니다. => 해결은 벡터 크기 늘리기 

틀렸습니다는 출력할때 '\n' 개행문자를 안넣어줘서 떳습니다. =>해결은 개행문자 넣기

 

참 여러방면으로 다양하게 틀려줬습니다. 

만약 게임이었으면 타이틀 모으는 재미라도 있었을텐데..

 

 

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

 

2042번: 구간 합 구하기

첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000,000)과 M(1 ≤ M ≤ 10,000), K(1 ≤ K ≤ 10,000) 가 주어진다. M은 수의 변경이 일어나는 횟수이고, K는 구간의 합을 구하는 횟수이다. 그리고 둘째 줄부터 N+1번째 줄

www.acmicpc.net

 

▲ top