SFPC/2023 SFPC

당일치기 전주 여행 (백트래킹)

jemin0619 2024. 1. 19. 20:23

 

A1: 당일치기 전주 여행 1

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

using namespace std;

int main() {
	int hh,mm,h,m;
	vector<vector<int>> arr(6);
	vector<int> gone; // 아직 안 간 곳 저장 
	scanf("%2d:%2d",&hh,&mm);
	scanf("%2d:%2d",&h,&m);
	
	for(int i=0; i<6; i++){
		for(int j=0; j<6; j++){
			int temp;
			cin >> temp;
			arr[i].push_back(temp);
		}
	}
	
	// [0,0] [0,1]
	// [1,0] ...
	// [2,0] ...
	
	int sum=0;
	int line=0;
	int temp;
	
	//0번째 줄부터 돌면서 최솟값을 찾음 
	//이때 최솟값의 인덱스가 line이거나, 이미 갔거나, 0이면 안됨.
	//최솟값을 sum에 더하고 line을 (현재 line 최솟값의 인덱스)로 함 
	
	//그 라인으로 이동해서 최솟값을 찾음(index가 line, 0, 이미 갔던곳이면 안됨)
	// 최솟값을 sum에 더하고 line을 (현재 line 최솟값의 인덱스)로 함
	
	//반복하다가 마지막 남은 line에 도착하면 종료.
	//종료 후 그 line의 0번째 인덱스 값을 sum에 더함
	
	while(gone.size()<5){
		int min = 21; //이동시간이 20분 이하 
		
		for(int i=1; i<6; i++){ //0은 생각안함 
 			if(i!=line && count(gone.begin(),gone.end(),i)==0 && min>arr[line][i]){
 				 min = arr[line][i];
 				 temp = i;
			 }
		}
		line = temp;
		sum+=min;
		gone.push_back(temp); //gone 배열에 간 곳 추가
		if(sum<0) return 0;	
	}
	sum += arr[line][0];
	
	//cout << sum << "\n";
	
	int time = (h*60+m) - (hh*60+mm) - sum;
	printf("%02d:%02d",time/60,time%60);
	
    return 0;
}

 

이렇게 코드를 짰었는데 테스트케이스 2와 답이 달랐다...

 

그래서 대회가 끝나고 정답지를 보니 5중 for문으로 모든 경우의 값들을 구해서 그 중 최솟값을 찾았었다.

범위가 크지 않을 땐 이런 식으로 풀어도 되겠다는 생각을 했다.

//2023 전북 SFPC A1 예제 코드
//https://drive.google.com/drive/folders/1LYvmdVuvezMZdvL-994Fkqwk45AEIEYr

#include<bits/stdc++.h>
using namespace std;
int A[6][6], B[6];
int main(){
	int h1, h2, m1, m2, t, i, j,i1,i2,i3,i4,i5, min1=24*60+10;
	scanf("%d:%d", &h1, &m1);
	scanf("%d:%d", &h2, &m2);
	t = h2*60+m2-h1*60-m1;
	for(i=0;i<6;i++){
		for(j=0;j<6;j++){
			scanf("%d", &A[i][j]);
		}
	}
	for(i1=1;i1<=5;i1++){
		for(i2=1;i2<=5;i2++){
			for(i3=1;i3<=5;i3++){
				for(i4=1;i4<=5;i4++){
					for(i5=1;i5<=5;i5++){
						if(i1!=i2 and i1!=i3 and i1!=i4 and i1!=i5 and i2!=i3 and i2!=i4 and i2!=i5 and i3!=i4 and i3!=i5 and i4!=i5){
							if(A[0][i1]+A[i1][i2]+A[i2][i3]+A[i3][i4]+A[i4][i5]+A[i5][0] < min1){
								min1 = A[0][i1]+A[i1][i2]+A[i2][i3]+A[i3][i4]+A[i4][i5]+A[i5][0];
							}
						}
					}
				}
			}
		}
	}
	t = t - min1;
	printf("%02d:%02d", t/60,t%60);
}

백트래킹을 배운 뒤에 이 문제는 백트래킹으로 풀 수 있을 것 같다고 생각하여 다시 풀어보았다.

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

using namespace std;

vector<vector<int>> input(6);
int arr[10];
int num[10];
bool isused[10];
int Min = 9999999;

void func(int k){
	if(k==6){
		int sum=0;
		for(int i=0;i<=4;i++){
			sum+=input[arr[i]][arr[i+1]];
		}
		sum+=input[arr[5]][0];
		if(sum<Min) Min=sum;
		return;
	}
	
	for(int i=1;i<=5;i++){
		if(!isused[i]){
			isused[i] = true;
			arr[k] = i;
 			func(k+1);
			isused[i] = false;
		}
	}
}

int main() {
	int hh,mm,h,m;
	scanf("%2d:%2d",&hh,&mm);
	scanf("%2d:%2d",&h,&m);
	
	for(int i=0; i<6; i++){
		for(int j=0; j<6; j++){
			int temp;
			cin >> temp;
			input[i].push_back(temp);
		}
	}
	
	func(1);

	int time = (h*60+m) - (hh*60+mm) - Min;
	printf("%02d:%02d",time/60,time%60);
	
    return 0;
}

위 코드와 동작은 같지만 개인적으로 5중 for문보다는 맘에 드는 코드다.