AIX 程式設計大賽---AIX正方形問題

gudesheng發表於2008-01-03

AIX 程式設計大賽---AIX正方形問題

作者:成曉旭

作為“演算法及實現”欄目的“拋磚引玉”之作,將自己2年多前實現的一個演算法放出來。有一年IBM出了這個Java程式設計競賽題,當時,自己花晚上時間用Java實現了。

[問題描述]:

任意給定一個正方形,將正方形的各邊做n等分,並將相應各點連線成水平或垂直的直線,如果從正方形的左下角(0,0)出發,沿各邊線或連線線,自左向右或自下而上的方向,到達正方形的右上角(n,n),請用JAVA程式計算並輸出所有可能的路徑總數和具體線路.請提供相關JAVA源程式和n=2,3,4時的輸出結果。輸出結果按以下方式:

n=1為例:

n = 1

Path1: (0,0) - (0,1) - (1,1)

Path2: (0,0) - (1,0) - (1,1)

Total = 2

 

[設計簡介]

共設計3個類:

AixPoint:正方形問題的低層處理類,抽象正方形問題的每個訪問點資訊;

AixSquare:正方形問題的核心處理類,抽象正方形演算法處理過程;

AixContest:正方形問題的客戶呼叫處理類,抽象正方形問題的應用層。

[演算法原始碼]

AixPoint原始碼:

/*******************************************************************************
 *    <>
 *            AIXContest  ver1.0
 *        開發作者:    成曉旭
 *        專案簡述:    AIX程式設計的Java程式設計"AIX正方形問題"解決方案
 *        啟動時間:    2004年01月14日    20:00:08
 *        完成時間:    2003年01月14日  20:09:00    <1個晚上>
 *
 *        開發環境:    Windows2000 Professional + SUN J2SE1.4.2
 *        開發工具:    Rational Rose2002 Enterprise + JCreator2.5Pro
 *
 *        檔名稱:    AixPoint.java
 *        簡    介:    正方形問題的低層處理類,抽象正方形問題的每個訪問點資訊
 *
 *        備    注:    
 *                        
 *        修改時間1:    
 *
 *****************************************************************************
*/

package    CXXSoft.Aix;
import java.awt.Point;

public class AixPoint extends Point
{
    
private int     moveUnit;
    
public boolean aheadExcess;
    
public boolean aboveExcess;
    
    
//類構造器
    public AixPoint()
    
{
        aheadExcess 
= false;
        aboveExcess 
= false;
        moveUnit 
= 1;
    }

    
    
//類構造器
    public AixPoint(int x,int y)
    
{
        
this();
        
this.x = x;
        
this.y = y;
    }

    
    
//類構造器
    public AixPoint(Point p)
    
{
        
this();
        
this.x = p.x;
        
this.y = p.y;
    }

    
    
//向左移動(前進)
    public void goAhead()
    
{
        
this.x += moveUnit;
    }

    
    
//向左的反方向移動(後退)
    public void goAheadReturn()
    
{
        
this.x -= moveUnit;
    }

    
    
//向上移動
    public void goAbove()
    
{
        
this.y += moveUnit;
    }

    
    
//向上的反方向移動(後退)
    public void goAboveReturn()
    
{
        
this.y -= moveUnit;
    }

    
    
//形成輸出串
    public String toString()
    
{
        
return "("+ x + "," + y +")";
    }

}

 

AixSquare原始碼:

/*******************************************************************************
 *    <>
 *            AIXContest  ver1.0
 *        開發作者:    成曉旭
 *        專案簡述:    AIX程式設計的Java程式設計"AIX正方形問題"解決方案
 *        啟動時間:    2004年01月14日    20:28:00
 *        完成時間:    2003年01月17日  00:16:00<4個晚上>
 *
 *        開發環境:    Windows2000 Professional + SUN J2SE1.4.2
 *        開發工具:    Rational Rose2002 Enterprise + JCreator2.5Pro
 *
 *        檔名稱:    AixSquare.java
 *        簡    介:    正方形問題的核心處理類,抽象正方形演算法處理過程
 *
 *        備    注:    
 *                        
 *        修改時間1:    
 *
 *        [問題描述]:
 *            任意給定一個正方形,將正方形的各邊做n等分,並將相應各點連線成水平
 *        或垂直的直線,如果從正方形的左下角(0,0)出發,沿各邊線或連線線,
 *        自左向右或自下而上的方向,到達正方形的右上角(n,n),
 *            請用JAVA程式計算並輸出所有可能的路徑總數和具體線路.
 *        請提供相關JAVA源程式和n=2,3,4時的輸出結果。輸出結果按以下方式:
 *      以n=1為例:
 *          n = 1
 *          Path1: (0,0) - (0,1) - (1,1)
 *          Path2: (0,0) - (1,0) - (1,1)
 *          Total = 2 
 *        
 *        [解答思路]:
 *            此問題的核心是一個"有向無環圖"的遍歷問題,
 *        解答的思想就是一個"試探"與"回溯"演算法的抽象與實現.甚至比完整的
 *        "有向無環圖"的遍歷問題還要簡單.
 *
 *        [建模提示]:
 *            為了簡化問題的處理過程,強調解決問題的實質性思路,在建模過程中,
 *        對遍歷過程中每步"前進"的步長進行了"固定步長為1"的假設,即對將被
 *        n等分的正方形的邊長,自動設定為n,以簡化演算法中對邊的計算處理.
 *        
 *        [演算法優化]:
 *             目前設計的演算法有以下幾處有待優化:
 *        1:此題是一般的"試探"與"回溯"演算法一個特例,所以有些處理過程是可以省略的
 *            (目前實現的是一個標準的"試探"與"回溯"演算法)
 *        2:由於題目自身的特殊性,對某些處理過程可做簡化,以提高演算法的執行效率
 *            (如:對進棧,出棧的處理,對訪問佇列的處理,對在正方形邊上"前進"時的
 *            回溯處理,對臨界條件,到達目標點條件的判斷等等)
 *        3:問題的本身及解答此題的思路是很具一般性的,但目前分析,設計的類結構過於特殊化.
 *
 *****************************************************************************
*/

package    CXXSoft.Aix;

import java.awt.Point;
import java.util.Stack;
import java.util.Vector;
import java.util.List;
import CXXSoft.Aix.AixPoint;

public class AixSquare
{
    
//AIX正方形問題點遍歷時前進方向常量定義
    private final static int MOVE_AHEAD = 1;    //向左
    private final static int MOVE_ABOVE = 2;    //向上
    
    
//AIX正方形問題點遍歷時優先前進的方向常量定義
    public final static int FIRST_MOVE_AHEAD = 101;    //先從左至右,後從下向上
    public final static int FIRST_MOVE_ABOVE = 102;    //先從下向上,後從左至右
    
    
private    int    moveUnit;            //當前演算法處理的單位長度
    private int nPart;                //矩形邊被等分的份數
    private int firstMoveWay;        //正方形問題線路優先前進方向
    private int nAccess;            //正確的通路總數
    private Point startP;
    
private Point endP;
    
private String strPath;
    
private Vector    visitedPoint;    //遍歷過程中已經訪問過的點佇列(每找到一條通道後被清空,然後重新載入)
    private    Stack    visitStack;        //遍歷過程中的堆疊
    private Vector    rightAccess;    //能到達目標點的正確通路佇列
        
    
//演算法訪問的當前點
    private AixPoint    p;
    
    
private void visitPoint()
    
{
        
if(strPath == null || strPath == "")
            strPath 
= "Path" + (nAccess + 1+"" + p;
        
else
            strPath 
+= " - " + p;
    }

    
    
//判斷是否向左前進越界
    private boolean isAheadExcess()
    
{
        
return (p.x > endP.x);
    }

    
    
//判斷是否向上前進越界
    private boolean isAboveExcess()
    
{
        
return (p.y > endP.y);
    }

    
    
//將當前輪的遍歷結果點組成的遍歷線路儲存於rightAccess中
    private void saveArriveLine()
    
{
        String str 
= "",strAccess = "";
        
for(int i=0;i<visitedPoint.size();i++)
        
{
            AixPoint q 
= (AixPoint)visitedPoint.get(i);
            str 
= (str == null || str == ""? " Path" + (nAccess + 1+"" : " - ";
            strAccess 
+= str + q;
        }

        rightAccess.add(strAccess);
    }

    
    
//判斷是否前進到目標點
    private boolean isArriveAim()
    
{
        
boolean isOK = false;
        isOK 
= ((p.x == endP.x) && (p.y == endP.y) 
              
&&(p.aheadExcess && p.aboveExcess));
        
if(isOK)
        
{
            saveArriveLine();
            nAccess
++;
            strPath 
= "";    
        }

        
return isOK;
    }

    
    
//遍歷的當前點進棧
    private void pushPoint()
    
{
        visitStack.push(p);
    }

    
    
//遍歷的當前點退棧
    private void popPoint()
    
{
        
if(!visitStack.empty())
        
{
            p 
= (AixPoint)visitStack.pop();
        }

    }

    
    
//修改遍歷堆疊中,引數指定的點的越界標誌
    private void setVisitStackExcess(AixPoint para,int flag)
    
{
        
for(int i=0;i<visitStack.size();i++)
        
{
            AixPoint q 
= (AixPoint)visitStack.get(i);
            
if(para == q)
            
{
                
switch(flag)
                
{
                    
case MOVE_AHEAD:
                        q.aheadExcess 
= true;
                        
break;
                    
case MOVE_ABOVE:
                        q.aboveExcess 
= true;
                        
break;
                }

                
            }

        }

    }

    
    
//遍歷的當前點進入佇列
    private void enterList()
    
{
        visitedPoint.add(p);
    }

    
    
//遍歷的當前點退出佇列(將當前點在遍歷佇列中刪除)
    private void exitList()
    
{
        visitedPoint.remove(p);
    }

    
    
//修改遍歷的當前點佇列中,引數指定的點的越界標誌
    private void setVisitedListExcess(AixPoint para,int flag)
    
{
        
for(int i=0;i<visitedPoint.size();i++)
        
{
            AixPoint q 
= (AixPoint)visitedPoint.get(i);
            
if(para == q)
            
{
                
switch(flag)
                
{
                    
case MOVE_AHEAD:
                        q.aheadExcess 
= true;
                        
break;
                    
case MOVE_ABOVE:
                        q.aboveExcess 
= true;
                        
break;
                }

            }

        }

    }

    
    
//判斷當前點是否已經在曾經訪問過的佇列中
    private boolean isVisited()
    
{
        
boolean isExist = false;
        
for(int i=0;i<visitedPoint.size();i++)
        
{
            
if(p == (AixPoint)visitedPoint.get(i))
            
{
                isExist 
= true;
                
break;
            }

        }

        
return isExist;
    }

    
    
//AIX正方形問題的"嘗試前進"方法
    private void attempt(int flag)
    
{
        AixPoint q 
= new AixPoint(p);
        p 
= q;
        
switch(flag)
        
{
            
case MOVE_AHEAD:    
                
//向左移動
                p.goAhead();    //[向前嘗試]
                break;
            
case MOVE_ABOVE:
                
//向上移動
                p.goAbove();    //[向上嘗試]
        }

    }

    
    
//AIX正方形問題的"回溯後退"方法
    private void backdate(int flag)
    
{
        popPoint();                
//[向後/向下回溯]
        pushPoint();
        setVisitedListExcess(p,flag);
        setVisitStackExcess(p,flag);
    }


    
//新版:goAlwaysLeft()方法
    protected void goAlwaysLeftNew()
    
{
        
        
if(!isVisited())
        
{
            pushPoint();
            enterList();
            visitPoint();
        }

        attempt(MOVE_AHEAD);
        
if(isAheadExcess())
            backdate(MOVE_AHEAD);
    }

    
    
//新版:goAlwaysUpper()方法
    protected void goAlwaysUpperNew()
    
{
        
if(!isVisited())
        
{
            pushPoint();
            enterList();
            visitPoint();
        }

        attempt(MOVE_ABOVE);
        
if(isAboveExcess())
            backdate(MOVE_ABOVE);
    }

    
    
//成功找到一條通道後,回退到下一個正確的新起點方法
    private void backdateNewStartPoint()
    
{
        
while((p.aheadExcess && p.aboveExcess))
        
{
            
if(p.x == startP.x && p.y == startP.y)
                
break;
            popPoint();
            
if((p.x == endP.x) && (p.y == endP.y))
                
continue;
            
if(p.aheadExcess && !p.aboveExcess)
                p.aboveExcess 
= true;
            
if(!p.aheadExcess && p.aboveExcess)
                p.aheadExcess 
= true;
            
if((!p.aheadExcess && !p.aboveExcess))
            
{
                
if((firstMoveWay == FIRST_MOVE_AHEAD) && (p.x <= startP.x))
                
{
                    p.aheadExcess 
= true;
                    
if(p.y >= endP.y)
                        p.aboveExcess 
= true;
                }

                
if((firstMoveWay == FIRST_MOVE_ABOVE) && (p.y <= endP.y))
                
{
                    p.aboveExcess 
= true;
                    
if(p.x >= endP.x)
                        p.aheadExcess 
= true;
                }

            }

        }

        
switch(firstMoveWay)
        
{
            
case FIRST_MOVE_AHEAD:
                p.aheadExcess 
= true;
                
break;
            
case FIRST_MOVE_ABOVE:
                p.aboveExcess 
= true;
                
break;
        }

        pushPoint();
    }

    
    
//成功找到一條通道後,從正確的新起點開始,將堆疊中所有的點轉存入遍歷佇列
    private void TransStackToNewList()
    
{
        visitedPoint.clear();
        
for(int i = 0; i < visitStack.size();i++)
            visitedPoint.add(visitStack.get(i));
    }

    
    
//正方形問題的沿當前線路前進的核心演算法
    private boolean advanceMoveKernel()
    
{
        
switch(firstMoveWay)
        
{
            
case FIRST_MOVE_AHEAD:
                
if(p.aheadExcess)
                    goAlwaysLeftNew();
                
else
                    goAlwaysUpperNew();
                
break;
            
case FIRST_MOVE_ABOVE:
                
if(p.aboveExcess)
                    goAlwaysUpperNew();
                
else
                    goAlwaysLeftNew();
                
break;    
        }

        
return isArriveAim();
    }

    
    
//類構造器
    public AixSquare()
    
{
        visitStack 
= new Stack();
        visitedPoint 
= new Vector();
        rightAccess 
= new Vector();
        startP 
= new Point();
        endP 
= new Point();
        moveUnit 
= 1;
        nAccess 
= 0;
        strPath 
= "";
    }

    
    
//類構造器
    public void setProblemCondition(int part,int firstMove)
    
{
        
this.firstMoveWay = firstMove;
        
if(part <= 0)
            part 
= 1;
        
this.nPart = part;
        endP.x 
= nPart;
        endP.y 
= nPart;
        
        nAccess 
= 0;
        strPath 
= "";
        visitStack.clear();
        visitedPoint.clear();
        rightAccess.clear();
    }

    
    
//新版:正方形問題解答方法
    public void solveProblemNew()
    
{
        
boolean arriveAim = false;
        p 
= new AixPoint(startP);
        
while(!p.aheadExcess || !p.aboveExcess)
        
{
            
while(!p.aheadExcess || !p.aboveExcess)
            
{
                
switch(firstMoveWay)
                
{
                    
case FIRST_MOVE_AHEAD:
                        
if(!p.aheadExcess)
                            goAlwaysLeftNew();
                        
else
                            goAlwaysUpperNew();
                        
break;
                    
case FIRST_MOVE_ABOVE:
                        
if(!p.aboveExcess)
                            goAlwaysUpperNew();
                        
else
                            goAlwaysLeftNew();
                        
break;    
                }

                arriveAim 
= isArriveAim();
                
if(arriveAim)
                    
break;
            }

            backdateNewStartPoint();
            TransStackToNewList();
            
if(visitStack.isEmpty() && (p.x == startP.x && p.y == startP.y))
                
break;
        }

    }

    
    
//類的處理結果輸出串
    public String toString()
    
{
        String str
=" n = " + nPart;
        
for(int i=0;i<rightAccess.size();i++)
        
{
            str 
+= rightAccess.get(i);
        }

        str 
+= " Total = " + nAccess;
        
return str;
    }

}

 

AixContest原始碼:

 

/*******************************************************************************
 *    <>
 *            AIXContest  ver1.0
 *        開發作者:    成曉旭
 *        專案簡述:    AIX程式設計的Java程式設計"AIX正方形問題"解決方案
 *        啟動時間:    2004年01月14日    20:00:00
 *        完成時間:    2003年01月14日  23:16:00<3個晚上>
 *
 *        開發環境:    Windows2000 Professional + SUN J2SE1.4.2
 *        開發工具:    Rational Rose2002 Enterprise + JCreator2.5Pro
 *
 *        檔名稱:    AixContest.java
 *        簡    介:    正方形問題的客戶呼叫處理類,抽象正方形問題的應用層
 *
 *        備    注:    
 *                        
 *        修改時間1:    
 *
 *        [問題描述]:
 *            任意給定一個正方形,將正方形的各邊做n等分,並將相應各點連線成水平
 *        或垂直的直線,如果從正方形的左下角(0,0)出發,沿各邊線或連線線,
 *        自左向右或自下而上的方向,到達正方形的右上角(n,n),
 *            請用JAVA程式計算並輸出所有可能的路徑總數和具體線路.
 *        請提供相關JAVA源程式和n=2,3,4時的輸出結果。輸出結果按以下方式:
 *      以n=1為例:
 *          n = 1
 *          Path1: (0,0) - (0,1) - (1,1)
 *          Path2: (0,0) - (1,0) - (1,1)
 *          Total = 2 
 *        
 *        [解答思路]:
 *            此問題的核心是一個"有向無環圖"的遍歷問題,
 *        解答的思想就是一個"試探"與"回溯"演算法的抽象與實現.甚至比完整的
 *        "有向無環圖"的遍歷問題還要簡單.
 *
 *        [建模提示]:
 *            為了簡化問題的處理過程,強調解決問題的實質性思路,在建模過程中,
 *        對遍歷過程中每步"前進"的步長進行了"固定步長為1"的假設,即對將被
 *        n等分的正方形的邊長,自動設定為n,以簡化演算法中對邊的計算處理.
 *        
 *        [演算法優化]:
 *             目前設計的演算法有以下幾處有待優化:
 *        1:此題是一般的"試探"與"回溯"演算法一個特例,所以有些處理過程是可以省略的
 *            (目前實現的是一個標準的"試探"與"回溯"演算法)
 *        2:由於題目自身的特殊性,對某些處理過程可做簡化,以提高演算法的執行效率
 *            (如:對進棧,出棧的處理,對訪問佇列的處理,對在正方形邊上"前進"時的
 *            回溯處理,對臨界條件,到達目標點條件的判斷等等)
 *        3:問題的本身及解答此題的思路是很具一般性的,但目前分析,設計的類結構過於特殊化.
 *****************************************************************************
*/


package    CXXSoft.Aix;
import java.awt.Point;
import CXXSoft.Aix.AixSquare;

public class AixContest
{    
    
public static void main(String[] arg)
    
{
        System.out.println(
"AIX知識大賽初賽Java程式設計---AIX正方形問題");
        
        
        AixSquare asProblem 
= new AixSquare();
        String str
="";
        
for(int i=1;i<=3;i++)
        
{
            asProblem.setProblemCondition(i,AixSquare.FIRST_MOVE_AHEAD);
            asProblem.solveProblemNew();
            str 
+= asProblem + " ";
        }

        System.out.println(str);
        System.out.println(
" AIX正方形問題---執行結束......");
    }

}


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1108610


相關文章