白駿15685号:龍曲線






問題の説明

  • https://www.acmicpc.net/problem/15685
  • 条件に従って線分を拡張し続ける問題.
  • 条件は最後の端点を90度回転させること.
  • 方法


  • 問題の核心は、回転座標90度とオブジェクト数の増加です.
    1.回転座標系
  • 以前はこの方式で座標回転を表していた.(前のプールのaとbは絶対値ではありません.)
  • この問題を解いて、絶対値で距離+または-を求める方が便利だと思います.
  • (2,3),(9,11)より、(2,3),(2+7,3+8)の方がわかりやすいです.
  • 2.オブジェクト数を増やす
  • 世代により、2^ageラインが生成されます.
  • 回転の中心軸がリストの最後の値、
  • リストの後ろからデータ(中心軸を除く)を読み込んで回転させることで、リストのデータが正しい順序で積み上げられます.
  • これらの属性は当初Dequeを使用することを考慮していたが、特定のインデックスの値を取得する必要があるため、LinkedListを使用した後にpollLast機能を直接実現する.
  • すなわち(A,B,C)データは、Cを中心とした軸を選択し、B回りに回転するB"を先に入れ、A回りに回転するA"(A,B,C,B",A")を入れてこそ、問題の場合と同じである.

  • 注意事項
  • 方向混同
  • 座標系がおかしい.(row,col)でもなく、ヒントの1象限も一般的な(x,y)座標系ではありません.y増加の方向は↓の問題
  • 回転が再実現されれば、点数を求める際にプレートの方向をx,yの方向に揃える必要はない.
  • 範囲
  • 0<=x,y<=100.100以外の範囲を慣性的に考えるとエラーが発生します.
  • 2点がx軸またはy軸に平行
  • 特別な処理は不要.4つの条件文はそれぞれ1つのxまたはyの同じ条件を受ければよい.
    以下のように設定します.こうしてもかまわないMov.x>=Std.x && Mov.y>Std.y // Mov.x>Std.x && Mov.y>=Std.y Mov.x<Std.x && Mov.y>=Std.y // Mov.x<=Std.x && Mov.y>Std.y Mov.x<=Std.x && Mov.y<Std.y // Mov.x<Std.x && Mov.y<=Std.y Mov.x>Std.x && Mov.y<=Std.y // Mov.x>=Std.x && Mov.y<Std.y
  • 回転座標が紛らわしくてエラーが発生しやすい.紙に描くことを強くお勧めします
  • コード実装では本当にエラーが多い.モジュール化された操作を可能な限り行い、実装し、すぐに出力して、結果がお客様の要件に合致することを強くお勧めします.
  • pseudocode

    Main(){
        for(DragonCurve의 개수){
            주어진 입력마다 DragonCurve를 실행합니다.
        }
    	Score() // 모든 커브를 완성한 후 점수를 계산합니다.
    }
    
    
    DragonCurve(x,y,d,age){
        좌표를 저장하는 list를 생성합니다.
        (x,y)(x+dx[d],y+dy[d])0세대를 list에 담습니다.
        for(1부터 age까지){
            현재 list에서 가장 뒤에 있는 값을 기준점으로 선정합니다.
            for(기준점 직전의 좌표->가장 처음좌표 순서로){
                list.add(Turn(기준점, 현재좌표)) // 현재좌표를 90도 회전한 좌표를 list에 넣습니다. (addLast)
            }
        }
    }
    
    Turn(기준점,돌려야 하는 점){
    	xDist = 두 점의 x값 차이;
        yDist = 두 점의 y값 차이;
        if(돌려야 하는 점이 기준점의 오른쪽 아래에 있으면){
        	새로운 점 = 기준점.x-yDist,기준점.y+Xdist;
        }
    	if(돌려야 하는 점이 기준점의 왼쪽 아래에 있으면){
        	새로운 점 = 기준점.x-yDist,기준점.y-Xdist;    
        }
        if(돌려야 하는 점이 기준점의 왼쪽 위에 있으면){
        	새로운 점 = 기준점.x+yDist,기준점.y-Xdist;
        }
        if(돌려야 하는 점이 기준점의 오른쪽 위에 있으면){
        	새로운 점 = 기준점.x+yDist,기준점.y+Xdist;
        }
        return 새로운 점
    }
    
    Score(){
    	for(0부터99까지){
        	for(0부터99까지){
            	if(4개의 점이 모두 true라면) cnt++;
            }    
        }
    	return cnt;
    }

    正解

    import java.util.*;
    
    public class Main {
    	static int[] dx = {1,0,-1,0};
    	static int[] dy = {0,-1,0,1};
    	static boolean[][] board;
    	public static void main(String[] args) {
    		Scanner sc = new Scanner(System.in);
    		board = new boolean[101][101];
    		int N = sc.nextInt();
    		for (int i = 0; i < N; i++) {
    			int x = sc.nextInt();
    			int y = sc.nextInt();
    			int d = sc.nextInt();
    			int age = sc.nextInt();
    			DragonCurve(x,y,d,age);
    		}
    		System.out.println(Score());
    	}
    	
    
    	public static void DragonCurve(int x, int y, int d, int age) {
    		List<pos> lst = new LinkedList<pos>();
    		// 0세대
    		lst.add(new pos(x,y));
    		lst.add(new pos(x+dx[d],y+dy[d]));
    		
    		for (int a = 1; a <= age; a++) {
    			int size = lst.size();
    			pos Std = lst.get(size-1);// 기준점
    			for (int i = size-1-1; i >=0; i--) { // 기준점을 제외한 lst의 모든 원소를 의미합니다.
    				lst.add(Turn(Std,lst.get(i))); // 회전한 좌표를 lst에 넣습니다.
    			}
    		}
            
            // 드래곤커브를 완성 후 board에 좌표를 표시하는 작업입니다.
    		for (int i = 0; i < lst.size(); i++) {
    			pos temp = lst.get(i);
    			if(0<=temp.x && temp.x < 101 && 0<=temp.y && temp.y < 101) { 
    				board[temp.x][temp.y] = true;
    			}
    		}
    		
    	}
    	
    	public static pos Turn(pos Std, pos Mov) {
    		int xDist = Math.abs(Std.x-Mov.x);
    		int yDist = Math.abs(Std.y-Mov.y);
            // 중심축을 기준으로 비교대상이 어디 위치하느냐에 따라 결과값이 다릅니다.
    		if(Mov.x>=Std.x && Mov.y>Std.y) { // 좌표계 회전에 첨부한 그림을 기준으로 회전할 좌표가 4사분면에 있으면
    			return(new pos(Std.x-yDist,Std.y+xDist));
    		}
    		if(Mov.x<Std.x && Mov.y>=Std.y) { // 좌표계 회전에 첨부한 그림을 기준으로 회전할 좌표가 3사분면에 있으면
    			return(new pos(Std.x-yDist,Std.y-xDist));
    		}
    		if(Mov.x<=Std.x && Mov.y<Std.y) { // 좌표계 회전에 첨부한 그림을 기준으로 회전할 좌표가 2사분면에 있으면
    			return(new pos(Std.x+yDist,Std.y-xDist));
    		}
    		if(Mov.x>Std.x && Mov.y<=Std.y) { // 좌표계 회전에 첨부한 그림을 기준으로 회전할 좌표가 1사분면에 있으면
    			return(new pos(Std.x+yDist,Std.y+xDist));
    		}
    		return Std;
    	}
    	
    	public static int Score() {
    		int cnt = 0;
    		for (int i = 0; i <= 99; i++) {
    			for (int j = 0; j <= 99; j++) {
    				if(board[i][j] && board[i+1][j] && board[i][j+1] && board[i+1][j+1]) cnt++;
    			}
    		}
    		return cnt;
    		
    	}
    	
    	static class pos{
    		int x;
    		int y;
    		public pos(int x, int y) {
    			super();
    			this.x = x;
    			this.y = y;
    		}
    		@Override
    		public String toString() {
    			return "pos [x=" + x + ", y=" + y + "]";
    		}
    	}
    }
    

    その他

  • 頂点ではなく線分がつながっている場合はどうなりますか?