Java basic | Java OOP | โครงสร้างข้อมูล | โจทย์ ACM



[JAVA] Inception Watch step by step


วันนี้เราจะมาทำโจทย์ Inception Watch แบบ step by step 

        โจทย์ Inception Watch สามารถดาวโหลดได้ที่ลิงค์ http://goo.gl/48Zvud
จากบทความเดิมที่ผมเคยเขียนไว้ ACM InceptionWatchQ JAVA วิธีทำ คือ ให้คำนวณกลับจากชั้นสุดท้ายมายังชั้นแรก ในชั้นสุดท้าย (level n) จะคำนวณเวลาของ Tsk ที่ชั้นนั้นได้โดยตรง ส่วนชั้น n-1 ต้องเอาเวลาทั้งหมด(elapsed time) ของชั้น n หารด้วย 12 แล้วบวกกับ Tnext ของชั้นนั้น ก็จะได้ Tsk ของชั้น n-1
อ่านแล้วคงรู้งงๆมาลองคำนวน เพื่อจะช่วยให้เห็นภาพมากขึ้น ซึ่งมองภาพการเขียนโปรแกรมของเราได้ชัดเจนมากยิ่งขึ้นด้วย

1.การวิเคราะห์โจทย์และลองคำนวณผลลัพธ์ด้วยมือก่อนเขียนโปรแกรม

1.1 แปลงเวลาทั้งหมดให้เป็นหน่วยวินาทีทั้งหมดของทุกๆชั้น

 โดยอ้างอิงจากโจทย์ตัวอย่างต่อไปนี้

   0 0 s
   1 48 m
   2 6 h
   3 2 d
   4 24 d
  -1

หน่วยเวลาทีควรรู้นะครับ 

   1 วินาที(s) = 1 
   1 นาที(m) = 60 วินาที
   1 ชั่วโมง(h) = 3600 วินาที ->ได้จาก  60*60
   1 วัน(d) = 86400 วินาที ->ได้จาก 24*60*60
   1 ปี(y) = 31536000 วินาที  ->ได้จาก 365*24*60*60

ผลจากการแปลงให้อยู่ในหน่วย วินาที ก็จะได้ตัวเลขดังนี้
   0 0 
   1 2880 
   2 21600
   3 172800
   4 2073600

1.2 การคำนวน inception watch โดยใช้สูตร


         sum = sum / 12 +incept[i - 1].getNumSecond();


    โดยกำหนดให้          
            sum                                คือ  ตัวแปรสำหรับเก็บผลลัพธ์
            Incept[i - 1].getNum()    คือ  จำนวนเวลาในหน่วยวินาที ในชั้นรองจากชั้นปัจจุบันที่เราอยู่
            i                                       คือ  ชั้นปัจจุบันที่เราอยู่
            getNumSecond ()          คือ  เป็นฟังก์ชันก์สำหรับการดึงจำนวนเวลาในหน่วยวินาทีของชั้น i -1
            Incept                              คือ  เป็น object ที่เก็บลำดับชั้นจำนวนเวลาในวินาที

วิธีทำ 

เริ่มจากชั้นที่ 5
         ค่า ณ ปัจจุบัน sum = 0 , Incept[i - 1].getNum() = 2073600, i = 5
         แทนค่า sum = 0/ 12 + 2073600
                      sum =  2073600;
ชั้นที่ 4 
         ค่า ณ ปัจจุบัน sum =  2073600  , Incept[i - 1].getNum() = 172800 , i = 4
         แทนค่า sum =  2073600  / 12 +  172800  
                      sum = 345600

ชั้นที่ 3 
         ค่า ณ ปัจจุบัน sum =   345600   , Incept[i - 1].getNum() =  21600 , i = 3
         แทนค่า sum =   345600   / 12 +    21600  
                      sum =   50400

ชั้นที่ 2
         ค่า ณ ปัจจุบัน sum =    50400    , Incept[i - 1].getNum() = 2880 , i = 2
         แทนค่า sum =    50400   / 12 +    2880 
                      sum =   7080

ชั้นที่ 1
         ค่า ณ ปัจจุบัน sum =     7080     , Incept[i - 1].getNum() = 0 , i = 1
         แทนค่า sum =  7080   / 12 +    0 
                      sum =  590
 เป็นอันเสร็จการคำนวน แล้วเราจะมาแสดงค่ายังไงล่ะ   เราก็เรียงจากด้านล่างไปด้านหน้าก็จบ 

        590
        7080
        50400
        345600
        2073600

ส่วนการทำให้ได้ตัวเลขแสดง 10 หลักอ่านลงไปด้านล่างของบทความก็จะเจอครับ


2.การออกแบบการเขียนโปรแกรมที่เราได้วิเคราะห์มา


       หลังที่เราวิเคราะห์โจทย์แล้ว เราวิเคราะห์ในส่วนของโปรแกรมที่เราจะเขียน โดยเราจะใช้หลักการ OOP เข้ามาเป็นหลักเกณฑ์ในการคิด คือ class InceptinWatch ควรจะมีคุณสมบัติอะไรบ้าง (attribute หรือจะเรียกว่า ตัวแปรก็ได้นะ) มีการกระทำอะไรบ้าง(Method) หรือ จะเรียกว่า ฟังก์ชั่นการทำงาน 
       class InceptinWatch ควรจะมีคุณสมบัติดังนี้ คือ

        attribute 
           int level = 0;                          //เอาไว้เก็บ level หรือลำดับชั้น
           int numSecond= 0;             //เอาไว้เก็บผลการแปลงเวลาในหน่วยวินาทีของ object

       Method 
        convertToSecondUnit(int numc,char c)  //Method สำหรับแปลงเวลาให้อยู่ในหน่วยวินาที
         position9(String s)                    //Method สำหรับ ทำตำแหน่งตัวเแลข 9 ตำแหน่ง
         main(String[] args)                   //เอาไว้ทำงานในส่วน main ของโปรแกรม

       ตามความคิดผมว่า...ที่สำคัญๆ หลักๆของโปรแกรมเราควรจะมีแค่นี้ล่ะครับซึ่งเวลาเราเขียนจริงๆ ก็จะเกิดไอเดียร์ใหม่ๆ ทำให้สามารถเพิ่มหรือลดความยาวของบรรทัดของโปรแกรมได้ครับ

3. เริ่มเขียนโปรแกรม 

      จากที่ได้วิเคราะห์และคำนวนจนได้ผลลัพท์เรียบร้อยแล้ว ต่อไปเรามาเริ่มเขียนโปรแกรมกันแล้วนะครับ ขั้นแรกก็ New Project


รูปการ New Project ใหม่
              พอคลิก New Project ก็จะแสดงหน้าต่างให้เรา ตั้งชื่อโปรเจก ในที่นี้ ตั้งชื่อเป็น InceptionWatchQ
รูป การตั้งชื่อโปรเจก
            จากรูปผมเอา Create Main Class เนื่องจาก ผมชอบพิมพ์เอง เสร็จแล้วก็กด finish ก็จะได้หน้าต่างนี้ขึ้นมา



ให้เราทำการ คลิกขวาที่ default package แล้วก็ new > Java Class



พอได้แล้วก็ ตั้งชื่อให้เหมือนกันกับชื้อโปรเจก ดังรูป แล้วก็กด finish

พอกดเสร็จ มันก็จะเจนค่าให้ดังรูป


ทีนี้ก็ได้ Class InceptionWatch มาแล้ว ซึ่งตอนนี้ยังไม่สามารถ run โปรเจกได้เนืองจากยังไม่มี Mathode main ให้เราพิมพ์ 

                 public static void main (String [] args){

                 }

ให้พิมพ์ ภายใต้ class InceptionWatch นะครับ ดังรูปด้านล่าง 




           ก่อนที่จะเริ่มเขียนโปรแกรมผมจะเช็คดูก่อนว่า โปรแกรมมีการรับค่าแสดงผลอย่างไร ซึ่งในโจทย์นี้มันจะเป็นการวนรับค่าไปเรื่อยๆ จนกว่า input ที่เข้ามาเป็น "-1" ค่อยแสดงผลลัพธ์ออกมา เรามาลองทำตามเงื่อนไขนี้ดู ซึ่งมันจะช่วยให้เรากำหนดรูปแบบโปรแกรมที่จะเขียนได้

สิ่งที่เราต้องการ
     - จะมีการรับค่ามาจากคีย์บอร์ด
     - วนรับค่าเรื่อยๆจนกว่า input เป็น " -1" ค่อยออกจากลูป
     - จะมีการเก็บค่าระหว่างที่รอให้ input เป็น "-1"
     - แสดงค่า ที่เก็บไว้เมื่อ input เป็น "-1"



จากรูป 
     - รับค่าเราใช้ Scanner ในการรับข้อมูลจากคีย์บอร์ด
     - การวนลูป รับค่าเรื่อยๆ เราใช้ while (true) 
     - การเก็บมูลระหว่างที่รอให้ input เป็น "-1" เราใช้ ArrayList<String> เป็น Array ชนิด String

       พอเราเข่าใจ input / output ของโปรแกรมแล้ว เราก็มาเขียนโปรแกรมตามที่เราวิเคราะห์ไว้ครับ

มาเขียนที่ล่ะ step ตามที่ผมทำ
     step 1  แปลงหน่วย m , h , d ให้อยู่ในหน่วย s แล้วก็เก็บค่า level กับ numSecond ใส่ในออปเจกให้ครบตาม input ที่เข้ามา แล้วก็แสดงค่าค่าทั้งหมดเมื่อ input = "-1"


   import java.util.Scanner;
   public class InceptionWatchQ {
    //attribute 
    private int level;        //เก็บ level
    private int numSecond;   //เก็บ ผลการแปลงเวลาในหน่วยวินาที ของ opject

    //methode สำหรับดึง ค่า level
    private int getlevel(){
        return level;
    }
    //สำหรับดึง ค่า numSecond
    private int getNumSecond(){
        return numSecond;
    }

    //แปลงเวลาให้อยู่ในหน่วยวินาที
     public int convertToSecondUnit(int numc,char c){
            switch(c){
                case 's': return numc *= 1;       // 1 วินาที
                case 'm': return numc *= 60;      //1 นาที = 60 วินาที
                case 'h': return numc *= 3600;    //1 ชั่วโมง = 3600 วินาที = 60*60
                case 'd': return numc *= 86400;   //1 วัน = 86400 วินาที = 24*60*60
                case 'y': return numc *= 31536000;//1 ปี = 31536000 วินาที = 365*24*60*60
                default : return numc = 0;
        }

       public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String input;                               //เอาไว้เก็บ input 
        String [] textIn = new String[3];           //เอาไว้เก็บ level , num , unit
        InceptionWatchQ[] incept = new InceptionWatchQ[10];  //เพราะว่า โจทย์กำหนด level 0-9
        int sum =0; //เก็บผลลัพธ์ ในการคำนวน
        int levels = 0; //นับจำนวนชั้น
        while (true) {  //วนรับค่าเรื่อยๆ
            input = sc.nextLine(); //รับค่ามาจากคีย์บอร์ด
            if (!input.equals("-1")) { // input ไม่เป็น " -1" 
                textIn = input.split(" ");//แยก level , num , unit
                incept[levels] = new InceptionWatchQ(Integer.parseInt(textIn[0]),Integer.parseInt(textIn[1]),textIn[2].charAt(0));     
                levels++;  //ก็ให้เก็บมูลลงให้ list
            } else {       // input เป็น " -1" ก็ให้แสดง list ทั้งหมด
                //print รายการทีเก็บไว้
                for(int i = 0 ; i< levels ; i++){
                    System.out.println(incept[i].getlevel()+ " " + incept[i].getNumSecond());
                }
            }
        }
    }
}
หมายเหตุ  
   - InceptionWatchQ[] incept = new InceptionWatchQ[10];  เป็นการสร้าง Opject แบบ Array โดยประกาศจอง array 10 ช่อง
   - incept[levels] = new                  InceptionWatchQ(Integer.parseInt(textIn[0]),Integer.parseInt(textIn[1]),textIn[2].charAt(0));   เป็นการเก็บค่า level ,num,unit ใส่ในออปเจก incept[levels]

ผลการรันโปรแกรม step 1



ผลการรัน step 1

           step 2 พอเราสามารถแปลงเวลาทั้งหมดอยู่ในวินาทีได้แล้ว ต่อไปก็จะเป็นการนำตัวเลขที่ได้มาคำนวนคำนวนตามสูตรที่เราได้ออกแบบไว้ 
/*
 * แก้ปัญหาโจทย์ Inception Watch
 * @author http://javaagkasit.blogspot.com
 */
import java.util.ArrayList;
import java.util.Scanner;

public class InceptionWatchQ {
    //attribute
    private int level;//เก็บ level
    private int numSecond;//เก็บ ผลการแปลงเวลาในหน่วยวินาที ของ opject
    //คอนทรักเจอร์
    public InceptionWatchQ(int _level,int _numSecond,char unit){
        this.level = _level;
        this.numSecond = convertToSecondUnit(_numSecond,unit);
    }
    //คอนทรักเจอร์ โอเว่อโลดดิ่ง
    public InceptionWatchQ(int _level,int _numSecond){
        this.level = _level;
        this.numSecond = _numSecond;
    }
    //สำหรับดึง ค่า level
    private int getlevel(){
        return level;
    }
    //สำหรับดึง ค่า numSecond
    private int getNumSecond(){
        return numSecond;
    }
    //แปลงเวลาให้อยู่ในหน่วยวินาที
    public int convertToSecondUnit(int numc,char c){
        switch(c){
            case 's': return numc *= 1;         // 1 วินาที
            case 'm': return numc *= 60;        //1 นาที = 60 วินาที
            case 'h': return numc *= 3600;      //1 ชั่วโมง = 3600 วินาที = 60*60
            case 'd': return numc *= 86400;     //1 วัน = 86400 วินาที = 24*60*60
            case 'y': return numc *= 31536000;  //1 ปี = 31536000 วินาที = 365*24*60*60
            default : return 0;
        }
    }
    //method main
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String input; //เอาไว้เก็บ input 
        String [] textIn = new String[3]; //เอาไว้เก็บ level , num , unit
        InceptionWatchQ[] incept = new InceptionWatchQ[10];//เพราะว่า โจทย์กำหนด level 0-9
        int sum =0; //เก็บผลลัพธ์ ในการคำนวน
        int levels = 0;//นับจำนวนชั้น
        ArrayList lists;   //ประกาศตัวเเปร list ไว้เก็บผลลัพท์
        
        while (true) {                      //วนรับค่าเรื่อยๆ
            input = sc.nextLine();          //รับค่ามาจากคีย์บอร์ด
            if (!input.equals("-1")) {      // input ไม่เป็น " -1" 
                textIn = input.split(" ");  //แยก level , num , unit
                incept[levels] = new InceptionWatchQ(Integer.parseInt(textIn[0]),Integer.parseInt(textIn[1]),textIn[2].charAt(0));     
                levels++;                   //ก็ให้เก็บมูลลงให้ list
            } else {                        // input เป็น " -1" ก็ให้แสดง list ทั้งหมด
                lists = new ArrayList();
                for(int i = levels; i > 0 ; i--){
                    sum = sum / 12 + incept[i - 1].getNumSecond(); //คำนวน
                    lists.add(new InceptionWatchQ(incept[i-1].level,sum)); //เก็บผลลัพธ์
                }
                //print รายการทีเก็บไว้
                for(int i = lists.size()-1 ; i >= 0  ; i--){
                    System.out.println(lists.get(i).level +" "+lists.get(i).numSecond);
                }
                //เคียร์ข้อมูลทั้งหมด
                lists.clear(); // เคลีย์ lists
                levels = 0; //เคลีย์ levels = 0
                sum =0; // เคลีย์  sum =0
            }
        }
    }
}
จาก code ด้านบน ตัวหนังสือสีแดงคือ คำสั่งที่เพิ่มขึ้นจาก step 1 
หมายเหตุ 
 ArrayList<InceptionWatchQ> lists;  //เป็นการเก็บข้อมูล ชนิดที่เป็น object 

ผลการรันโปรแกรม step 2



           step 3 จากนั้นก็เหลือแค่ ทำให้ตัวให้เป็น 10 วิธี ทำโดยการเอาตัวเลข lists.get(i).numSecond มาให้ให้เป็น 9 หลัก แล้วเอา 9 หลักที่ได้มารวมกันกับ lists.get(i).level ก็จะได้ตัวเลข 10 หลัก
//ทำตำแหน่งตัวเแลข 9 ตำแหน่ง
        public static  String position9(String s){
            char c = 0 ;
            for(int i = 0 ; i < 10;i++){
                if(i > s.length()){
                    s = "0"+s; 
                }
            }
            return s;
        }
หลักการคือ ถ้าค่า i มากว่า ความยาวของ s.length() ก็ให้เพิ่มเข้าไปหน้าของ ตัวเลข

ส่วนการใช้งาน methode position9() ก็ง่ายๆ 


//print รายการทีเก็บไว้
      for(int i = lists.size()-1 ; i >= 0  ; i--){
          System.out.println(lists.get(i).level +""+position9(lists.get(i).numSecond+""));
      }

ผลการรันโปรแกรม step 3

             เป็นอันเสร็จสิ้นการ เขียนโปรแกรม Inception Watch แบบ step by step จะเห็นได้ว่าทุกสิ่งอย่างที่เขียนไป มันขึ้นกับตัวผู้เขียนโปรแกรมเองว่ามีความเข้าใจโจทย์อย่างแท้จริงหรอยัง ถ้าคิดไม่ออกก็ถามพี่ google และที่สำคัญเราจะเขียนโปรแกรมได้สักโปรแกรมสิ่งแรกคือ จินตนาการ ใครที่ยังไม่เข้าก็ ดาวโหลดโปรเจกไปลองไล่ดูครับตามลิงค์นี้เลย http://www.mediafire.com/?qkp99h84kk9h5hq ผมก็จบเพียงแค่นี้ครับเจอกันใหม่บทความหน้า....

อาจอธิบายได้ไม่ละเอียดพอ ศึกษาเพิ่มได้ที่

         - การใช้ for http://javaagkasit.blogspot.com/2012/08/for.html
         - การใช้ switch casehttp://javaagkasit.blogspot.com/2012/08/switch-case.html
         - การใช้ if else http://javaagkasit.blogspot.com/2012/08/if-else.html
         - การใช้ while loop http://javaagkasit.blogspot.com/2012/09/while-loop.html
         - Class และ object คืออะไร http://javaagkasit.blogspot.com/2012/09/class-object.html
         - เข้าใจ Java method main
         - ArrayList คืออะไร
         - this คืออะไร
         - Constructor คืออะไรhttp://javaagkasit.blogspot.com/2012/09/constructor.html
         - Encapsulation คืออะไร 
         - การสร้างโปรเจกใน NetBeans IDEhttp://javaagkasit.blogspot.com/2012/09/netbeans-ide.html

share