1. 程式人生 > >O - The Troublesome Frog POJ - 1054 (多次剪枝)

O - The Troublesome Frog POJ - 1054 (多次剪枝)

In Korea, the naughtiness of the cheonggaeguri, a small frog, is legendary. This is a well-deserved reputation, because the frogs jump through your rice paddy at night, flattening rice plants. In the morning, after noting which plants have been flattened, you want to identify the path of the frog which did the most damage. A frog always jumps through the paddy in a straight line, with every hop the same length: 

 
Your rice paddy has plants arranged on the intersection points of a grid as shown in Figure-1, and the troublesome frogs hop completely through your paddy, starting outside the paddy on one side and ending outside the paddy on the other side as shown in Figure-2: 
 
Many frogs can jump through the paddy, hopping from rice plant to rice plant. Every hop lands on a plant and flattens it, as in Figure-3. Note that some plants may be landed on by more than one frog during the night. Of course, you can not see the lines showing the paths of the frogs or any of their hops outside of your paddy ?for the situation in Figure-3, what you can see is shown in Figure-4: 
 
From Figure-4, you can reconstruct all the possible paths which the frogs may have followed across your paddy. You are only interested in frogs which have landed on at least 3 of your rice plants in their voyage through the paddy. Such a path is said to be a frog path. In this case, that means that the three paths shown in Figure-3 are frog paths (there are also other possible frog paths). The vertical path down column 1 might have been a frog path with hop length 4 except there are only 2 plants flattened so we are not interested; and the diagonal path including the plants on row 2 col. 3, row 3 col. 4, and row 6 col. 7 has three flat plants but there is no regular hop length which could have spaced the hops in this way while still landing on at least 3 plants, and hence it is not a frog path. Note also that along the line a frog path follows there may be additional flattened plants which do not need to be landed on by that path (see the plant at (2, 6) on the horizontal path across row 2 in Figure-4), and in fact some flattened plants may not be explained by any frog path at all. 

Your task is to write a program to determine the maximum number of landings in any single frog path (where the maximum is taken over all possible frog paths). In Figure-4 the answer is 7, obtained from the frog path across row 6. 

Input

Your program is to read from standard input. The first line contains two integers R and C, respectively the number of rows and columns in your rice paddy, 1 <= R,C <= 5000. The second line contains the single integer N, the number of flattened rice plants, 3 <= N <= 5000. Each of the remaining N lines contains two integers, the row number (1 <= row number <= R) and the column number (1 <= column number <= C) of a flattened rice plant, separated by one blank. Each flattened plant is only listed once.

Output

Your program is to write to standard output. The output contains one line with a single integer, the number of plants flattened along a frog path which did the most damage if there exists at least one frog path, otherwise, 0.

Sample Input

6 7
14
2 1
6 6
4 2
2 5
2 6
2 7
3 4
6 1
6 2
2 3
6 3
6 4
6 5
6 7

Sample Output

7

 在存在n個腳印的格子內,是青蛙進過的痕跡,每次青蛙通過格子的路徑是直線的,求符合條件的最多點直線。

思路:按從左到右,從上到下的順序排序腳印的座標,然後列舉每個點,作為直線的第一個點,注意剪枝不可能或遍歷過情況,具體看程式碼

 

#include<cstdio>
#include<stack>
#include<set>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<iostream>
#include<cmath>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
const int N=5016;
const int nmax=11;
const double esp = 1e-9;
const double PI=3.1415926;
int r,c,n;
struct point
{
    int x,y;
} p[N];
int vis[N][N];
bool cmp(point p1,point p2)
{
    if(p1.y==p2.y)
        return p1.x<p2.x;
    return p1.y<p2.y;
}
int judge(int a,int b)
{
    if(a>0&&a<=r&&b>0&&b<=c)
        return 1;
    return 0;
}
int solve(int xx,int yy,int dx,int dy)
{
    int ans=0;
    while(judge(xx,yy))    //在格子內
    {
        if(!vis[xx][yy])
        {
            //不存在腳印,即直線不存在
            return 0;
        }
        ans++;
        xx+=dx;
        yy+=dy;
    }
    return ans;
}
int main()
{
    while(scanf("%d%d",&r,&c)!=EOF)
    {
        scanf("%d",&n);
        memset(vis,0,sizeof(vis));
        for(int i=0; i<n; i++)
        {
            scanf("%d%d",&p[i].x,&p[i].y);
            vis[p[i].x][p[i].y]=1;
        }
        sort(p,p+n,cmp);
        int sum=2;
        for(int i=0; i<n; i++) //以p[i]作為直線的第一個點
        {
            for(int j=i+1; j<n; j++) //以p[i]作為直線的第二個點
            {
                int dx=p[j].x-p[i].x;
                int dy=p[j].y-p[i].y;
                if(p[i].y+sum*dy>c)
                  /*如果以p[i]作為直線的第二個點的直線可能超過sum個點,那麼第sum+1個點應該在格子內,
                  否則這種情況是不存在多於sum個點的直線,同樣地,以p[i]為起點,該層迴圈後面的y值比p[j].y更大,
                  即每次跳的幅度更大了,那麼存在多於sum個點的直線更加不可能了,所以直接退出該層迴圈
                  */
                   break;
                if(judge(p[i].x+sum*dx,p[i].y+sum*dy)==0)
                    continue;  //再判斷x方向是否越界
                if(judge(p[i].x-dx,p[i].y-dy))
                  //p[i]前面有一個點在格子內,在前面已經計算過了
                    continue;
                sum=max(sum,solve(p[i].x,p[i].y,dx,dy));
            }
        }
        if(sum<3)
            printf("0\n");
        else
            printf("%d\n",sum);
    }
    return 0;
}