poj 1092 Farmland

Time Limit: 1000MSMemory Limit: 65536K
Total Submissions: 1238Accepted: 483

Description

We have a map for farming land in a country. The whole farming land of the country is divided into a set of disjoint farming regions. Each farmer owns only one farming region in this country. There is a boundary fence between two neighboring farming regions. The farmland map for this country can be represented in a plane graph. The following Figure-1 shows one example. 
poj <wbr>1092 <wbr>Farmland

Figure-1: Farmland graph G(V,E)

There are two types of edges, boundary edge and non-boundary edge. All edges of G(V,E) except (v8, v6) and (v11, v10) are boundary edges which are between two neighboring farming regions. The "proper farming region" in a Farmland graph is a closed region bounded by a simple cycle and it should not contain any vertices or edges inside. In this figure, the polygon < v1,v9,v8,v7 >is a proper farming region, and the region < v2, v1, v7, v8 , v2, v5, v4, v3 >is not a proper farming region since its boundary cycle is not simple. 
We assume that the farmland graph G(V,E) is a simple connected graph, which does not allow self-loops (Figure-2 (a)) and parallel edges (Figure-2 (b)).Also in Farmland graph G(V,E), we do not consider the outer face of G(V,E).You can see that there are 2 proper farming regions in G(V,E) shown in Figure-1,namely < v1,v9,v8,v7> and < v2,v3,v4,v5>, since there are no vertices or edges inside. But the polygon< v1,v7,v8,v2> is not a proper farming region since vertex v3, v4, and v5 are located in that region. Similarly, the region  is not a proper region because a vertex v10 is inside the region.A degenerate polygon < v6, v8> is not a proper region because it has no valid area inside.
poj <wbr>1092 <wbr>Farmland

Figure-2: (a) self-loop < v1,v1> , and (b) 3 parallel edges { < v1,v2>,< v1,v2>, < v1,v2>}

There are other assumptions for input farmland graph data. 
1. There is at least one proper farming region. 
2. The position of each vertex in Farmland graph is distinct. 
3. There is no edge crossing, which means the graph G(V,E) is a plane graph. 
4. Farmland graph G(V,E) is simple and connected. 

Let us define the "size" of proper farming region. The size of proper farming region is the number of boundary edges of that region. For example, the size of the proper farming region < v2,v3,v4,v5 > is 4. 
The problem is to find the number of proper regions that have a specified size.If you are requested to find the number of proper regions with size of 4 in the graph given in Figure-1, you must answer that there are 2 proper regions whose sizes are 4 because farming regions < v1,v9,v8,v7 > and < v2,v3,v4,v5 >are proper regions and their sizes are 4. If there are no such regions, then you have to print 0.

Input

The input consists of M test cases. The first line of the input contains a positive integer M, the number of test cases you are to solve. After the first line,input data for M cases follow. The first line of each test case contains a positive integer N (>=3), the number of vertices. Each of the following N lines is of the form:
i   x i    y i    d i    a1   a2   a3   .....   a di

"i" is the vertex number, xi and yi are the coordinate (xi, yi) of the vertex i, and di is the degree of the vertex i. The following { ai } are the adjacent vertices of the vertex i. The last line gives k, the size of proper regions that you have to count. 
Note that M, the number of cases in input is less than 10. N, the number of vertices of a given farmland graph is less than 200. All vertices are located on grid points of the 1000 x 1000 lattice grid.

Output

The output must contain M non-negative integers. Each line contains the answer n to the corresponding case of the input.

Sample Input

 
 
                 
12                    
 2 6   9 7 2 
 5 6   5 3 1 8   
 3 5   4 2       
 3 4   3 5       
 4 4   4 2 
 7 4   8 
 2 3   8 1 
 5 3   7 2 9 12 6 
 1 2   11 8 1 
10 3 2   11 
11 2 1    10 9 12 
12 6 1   8 11 
 
                    
 2 2   2 3 
 1 1   1 3 
 4 1   1 2 
4

 

Sample Output

2
0

Source




题目大意:给一个平面图,找边数为k且内部不包含其他点的简单多边形的个数。简单多边形的定义是多边形任意两条不相邻的边(包括顶点)不相交不重合。
//============================================================================

算法:枚举每一条边做为起始边每次找到极角排序后最右边的边,重复k次,判断是否为符合条件的简单多边形,是则计数器加1。

细节很麻烦。。。。

Q:最右边的边怎么定义?
A:首先,将所有边拆成两条有向边。因为找最右本身就是有方向的提法。
于是当前的边就有一个指向的点和一个指出的点,我们规定沿着边的方向找简单多边形。

将与指向的点相连且不是指出的点进行极角排序,顺序最小的点就是最右的点了。

或者可以这样理解:将当前边做为极轴,正方向取边的反向。所有与极点相连的边中极角最小的就是最右边。



由此易知:每次找最右的边,最后如果能得到的一个多边形,则一定是简单多边形。



但是还有很多细节。

判断时候有重边(比如一个边数为3的简单多边形会被判断成6或9之类的)是很好处理的,就是在搜索的时候记录找过了那些边和点,在继续搜索的时候排除这些情况就行了。

还有就是:
1.任意一个合法的简单多边形会被判断成k个符合条件的简单多边形。
2.一个合法的简单多边形至多只有一个点有多于两条边,那么会被判断成两个。

有向边的价值在这里就体现出来了,找到一个简单多边形后就可以把构成它的所有有向边删掉。
因为题目保证了图是平面图,那么一条边至多只会存在于两个简单多边形上,拆成有向边后就是一个了。这样对于第一种情况就可以解决了。
第二种情况在深搜的时候加个特判,是否所有反向边都已经被删掉了,如果是,那么就不计入答案。具体见代码吧。


CODE

#include

#include

#include

#define eps 1e-7

using namespace std;

bool tmp = 1;

struct Dot

{

    double x, y;

    Dot () {}

    Dot (double x, double y) : x(x), y(y) {}

    Dot operator + (Dot a)

    {

        return Dot (x + a.x, y + a.y);

    }

    Dot operator - (Dot a)

    {

        return Dot (x - a.x, y - a.y);

    }

} d[200];

 

int con[200][15];

bool vis_e[200][15];

bool vis_d[200];

bool insi[200];

int n, len, start;

bool graph[200][200];

bool anti;

 

//con是邻接表存边

//vis_evis_d记录删掉了哪些边和点

//graph是邻接矩阵储存的边,方便特判所有方向边是否都被删掉

//anti记录方向边是否都被删掉

//len即题目中的kn是点数,start记录起始点

 

 

double cross (Dot g, Dot h)    //叉积用于找最右边

{

    return g.x * h.y - g.y * h.x;

}

double mul (Dot g, Dot h)    //点积

{

    return g.x * h.x + g.y * h.y;

}

bool right (Dot g, Dot h, Dot s)    //找最右边

{

    double ss = cross (g, h);

    double f1 = mul (s, g);

    double f2 = mul (s, h);

    if (ss > eps)

        return 1;

    if (ss < -eps)

        return 0;

    if (f1 < f2)

        return 1;

    return 0;

}

bool dfs (int u, int g, int tmp)    //深搜不停向右

{

    int v = con[u][g];

    insi[v] = 0;

    vis_d[v] = 1;

    anti = anti & graph[v][u];    //判断方向边是否都被删

 

    for (int i=0; i

        if (cross(d[v] - d[u], d[i] - d[u]) > eps)

            insi[i]=0;

//判断多边形内是否有点

 

    if (tmp == len)

    {

        if (v != start)

            return 0;

        int flag = 0;

        for (int i = 0; i < n; i++)

            flag = flag | insi[i];    //多边形内不能有点

        if (!flag)

            vis_e[u][g] = 1,

            graph[u][v] = 1;

        return (flag ^ 1) & (anti ^ 1);    //反向边

    }

 

    int t = 0;

    for (int i = 0; i < con[v][0]; i++)

    {

        int w = con[v][i+1];

        if (vis_d[w] || vis_e[v][i+1] || w == u)

            continue;

        if (cross (d[u] - d[v], d[w] - d[u]) > -eps

            && (!t || right (d[w] - d[v], d[con[v][t]] - d[v], d[u] - d[v])))

                t = i + 1;

    }

    if (!t)

        return 0;

    int flag = dfs (v, t, tmp + 1);

    if (flag)

        vis_e[u][g] = 1,

        graph[u][v] = 1;

    return flag & (anti ^ 1);

}

int main()

{

    freopen("1.in","r",stdin);

    freopen("1.out","w",stdout);

    int ttt; scanf("%d", &ttt);

    while (ttt--)

    {

        memset(con, 0, sizeof(con));

        scanf("%d", &n);

        for (int i=0; i

        {

            int x;

            scanf("%d", &x);

            x--;

            scanf("%lf %lf", &d[x].x, &d[x].y);

            scanf("%d",&con[x][0]);

            for (int j = 0; j < con[x][0]; j++)

            {

                scanf("%d", &con[x][j+1]);

                con[x][j+1]--;

            }

        }

        scanf("%d", &len);

 

        memset(graph, 0, sizeof (graph));

        memset(vis_e, 0, sizeof (vis_e));

//边删掉了就永远都不会再用了

        int ans = 0;

        for (int i=0; i

            for (int j=0; j

                if (!vis_e[i][j + 1])

                {

                    memset(vis_d, 0, sizeof(vis_d));

                    memset(insi, 1, sizeof(insi));

//点要每次删除,因为一个点可能出现在多个简单多边形上

 

                    anti = 1;

                    start = i;

                    insi[i] = 0;

                    if (dfs(i, j + 1, 1))

                        ans++;

                }

        printf("%d\n", ans);

    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值