問題:給出一系列的坐標點,請找出哪些點可以圍成一個面積最大的凸多邊形?
思路:(1)先尋找最左邊的坐標點,這樣剩下的點與這個點都可以連成一條直線,尋找斜率最大的點(x0,y0),這個點就是需要找的。
(2)以這個點(x0,y0)為基礎,按照上述方法尋找下一個點,以此類推,直到形成一個密閉的多邊形。
(3)在尋找點的過程中要保證這個過程是順時針方向進行。
方法:我們將以一組坐標值為例來講解算法的實現。如下圖所示,從坐標(-1,-1),(1,1),(1,3),(2,2),(2,1),(3,1),(4,-1)中找到可以圍成最大凸多邊形的坐標組。

(1)首先我們應該考慮到在用斜率來確定下一個坐標時,斜率值為無窮的情況(也就是要找的下一個坐標點的x值與原坐標點一樣)。其實通過上圖我們可以發現,在尋找下一個坐標點的時候,只有在最左邊和最右邊才會出斜率為無窮的情況。因為位於中間的坐標點總是以順時針方向尋找下一個點,不可能出現原坐標點和下一個坐標點的x值相同的情況。而位於兩邊的坐標則會出現這種情況,但是這種情況也比較特殊,因為出現在最左邊和最右邊上的坐標點全部都應該是最大凸多邊形上的點。因此我們可以先確定最左和最右為凸多邊形上的點,剩下的點則以”思路“中提供的方法來尋找。

(3)接下來將順時針尋找坐標點的過程,分為從左到右尋找上邊界的過程和從右到左尋找下邊界的過程。從左到右過程的起始坐標應該為最左邊的坐標中(若存在多個)縱坐標最大的那個(也就是圖示中的(1,3)),結束坐標應該為最右邊的坐標中縱坐標最大的那個。而從右到左過程的起始坐標點則分別是最右邊和最左邊最小的兩個坐標。


(3)最后求得的中間坐標和最左和最右坐標就是組成最大凸多邊形的所有坐標點。

Java代碼實現:
輸入:7;-1,-1;1,1;1,3;2,2;2,1;3,1;4,-1
輸出:6;-1,-1;1,1;1,3;2,2;3,1;4,-1
import java.awt.Point; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Scanner; import java.util.StringTokenizer; public class Main { public static Map<Integer, ArrayList<Point>> readData(){ Scanner s = new Scanner(System.in); String input = null; Map<Integer, ArrayList<Point>> pts = new HashMap<Integer, ArrayList<Point>>(); if(s.hasNext()){ input = s.nextLine(); } StringTokenizer st = new StringTokenizer(input, ",;"); st.nextToken(); while(st.hasMoreTokens()){ Integer x = Integer.parseInt(st.nextToken()); Integer y = Integer.parseInt(st.nextToken()); ArrayList<Point> v = pts.get(x) == null ? new ArrayList<Point>() : pts.get(x); v.add(new Point(x, y)); pts.put(x, v); } int key = 0; Map<Integer, ArrayList<Point>> indexPts = new HashMap<Integer, ArrayList<Point>>(); for(java.util.Map.Entry entry : pts.entrySet()){ indexPts.put(key, (ArrayList<Point>)entry.getValue()); key++; } return indexPts; } @SuppressWarnings({ "rawtypes", "unchecked" }) public static void main(String[] args) { ArrayList<Point> ptOkList = new ArrayList<Point>(); Map<Integer, ArrayList<Point>> pts = readData();//讀取數據並存儲到map中 int i = 0; Point startUp = pts.get(0).get(0); Point startBottom = pts.get(0).get(0); Point endUp = pts.get(pts.size() - 1).get(0); Point endBottom = pts.get(pts.size() - 1).get(0); for(i = 0; i < pts.get(0).size(); i++){ if(pts.get(0).get(i).y > startUp.y){ startUp = pts.get(0).get(i); } if(pts.get(0).get(i).y < startBottom.y){ startBottom = pts.get(0).get(i); } } for(i = 0; i < pts.get(pts.size() - 1).size(); i++){ if(pts.get(pts.size() - 1).get(i).y > startUp.y){ endUp = pts.get(pts.size() - 1).get(i); } if(pts.get(pts.size() - 1).get(i).y < startBottom.y){ endBottom = pts.get(pts.size() - 1).get(i); } } Point startPt = startUp; int index = 0; Point p = startUp; while(true){ float k = -Float.MAX_VALUE; for(i = index + 1; i < pts.size(); i++){ //int index_ = index; for(int j = 0; j < pts.get(i).size(); j++){ Point temp = pts.get(i).get(j); float kk = (temp.y - startPt.y) / (temp.x - startPt.x); if(kk > k){ p = temp; k = kk; index = i; } } } startPt = p; if(startPt == endUp){ break; } ptOkList.add(startPt); } startPt = endBottom; index = pts.size() - 1; p = endBottom; while(true){ float k = -Float.MAX_VALUE; for(i = index - 1; i >= 0; i--){ //int index_ = index; for(int j = 0; j < pts.get(i).size(); j++){ Point temp = pts.get(i).get(j); float kk = (temp.y - startPt.y) / (temp.x - startPt.x); if(kk > k){ p = temp; k = kk; index = i; } } } startPt = p; if(startPt == startBottom){ break; } ptOkList.add(startPt); } for(i = 0; i < pts.get(0).size(); i++){ ptOkList.add(pts.get(0).get(i)); } for(i = 0; i < pts.get(pts.size() - 1).size(); i++){ ptOkList.add(pts.get(pts.size() - 1).get(i)); } System.out.print(ptOkList.size()); for(i = 0; i < ptOkList.size(); i++){ System.out.print(";" + ptOkList.get(i).x + "," + ptOkList.get(i).y); } } }
