关于IntersectingConvexHull

时间:2016-09-19 02:36:33

标签: c++ algorithm

四小时前我第一次问这个问题。事实上,我已经搜索了这个问题超过6个小时,但仍然无法理解。

这个问题是通过给你x [n]和y [n]给你n分。您应该找到这些点的两个子集,其凸包相交。您的回答应该是满足上述规则的案件数量。

  

你在平面上给出了一组有限的点数。对于每个有效的i,   其中一个点具有坐标(x [i],y [i])。要点都是   截然不同,没有三个是共线的。

     

下面,CH(s)表示集合s的凸包:即,   包含集合s的所有凸多边形中的最小值。我们这么说   如果满足以下条件,则有序对(s1,s2)很有意思   很满意:

     

1.s1是S

的子集      

2.s2是S

的子集      

3.集合s1和s2是不相交的(即,它们没有共同的元素)

     

4.凸包CH(s1)和CH(s2)的交点具有正区域注意,来自S的某些点可能仍未使用(即,   他们既不会在s1中,也不会在s2中。你得到了   所有点的坐标:s x和y。请计算并返回   有趣的一对集的数量,模10 ^ 9 + 7。

     

示例

     

{1,0,-1,-1,0,1} {1,2,1,-1,-2,-1}

     

返回:14

     

我们有14个   解决方案:

     

s1 = {0,1,3},s2 = {2,4,5} s1 = {0,2,3},s2 = {1,4,5} s1 =   {0,1,4},s2 = {2,3,5} s1 = {0,2,4},s2 = {1,3,5} s1 = {1,2,4},s2 =   {0,3,5} s1 = {0,3,4},s2 = {1,2,5} s1 = {1,3,4},s2 = {0,2,5} s1 =   {0,2,5},s2 = {1,3,4} s1 = {1,2,5},s2 = {0,3,4} s1 = {0,3,5},s2 =   {1,2,4} s1 = {1,3,5},s2 = {0,2,4} s1 = {2,3,5},s2 = {0,1,4} s1 =   {1,4,5},s2 = {0,2,3} s1 = {2,4,5},s2 = {0,1,3}

有许多解决方案我无法理解,以下是其中之一。例如,ccw的用途是什么?结果由两部分组成,为什么? 你能给我一些算法名称,一些关键词也行,所以我可以在google上详细搜索吗?

以下是一个解决此问题的示例代码:

#include <vector>
#include <iostream>

using namespace std;
const long long mod=1000000007ll;
struct IntersectingConvexHull{
    public:
        int count(vector<int> x, vector<int> y){
            int n = x.size();
            long long P2[110];
            P2[0]=1ll;
            for(int i=1;i<=n;i++){
                P2[i]=P2[i-1]*2%mod;
            }
            long long C[110][110];
            for(int i=0;i<=n;i++){
                C[i][0]=C[i][i]=1ll;
                for(int j=1;j<i;j++){
                    C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
                }
            }
            long long X[100],Y[100];
            for(int i=0;i<=n;i++){
                X[i]=x[i];
                Y[i]=y[i];
            }
            long long ans=0;
            for(int i=0;i<n;i++){
                for(int j=0;j<n;j++){
                    if(i==j)continue;

                    int c1=0,c2=0;
                    for(int k=0;k<n;k++){
                        if(k==i||k==j){
                            continue;
                        }
                        long long ccw=(X[i]-X[k])*(Y[j]-Y[k])-(Y[i]-Y[k])*(X[j]-X[k]);
                        if(ccw<0){
                            c1++;
                        }
                        else{
                            c2++;
                        }
                    }
                    if(c1>=2&&c2>=2){
                        ans+=((P2[c1]+mod-c1-1)%mod)*((P2[c2]+mod-c2-1)%mod)%mod;
                        ans%=mod;
                    }
                }
            }
            long long A=0ll;
            for(int i=3;i<=n;i++){
                for(int j=3;j<=n-i;j++){
                    A+=C[n][i]*C[n-i][j]%mod;
                    A%=mod;
                }
            }
            return (A+mod-ans)%mod;

        }
};

1 个答案:

答案 0 :(得分:1)

两个集合必须至少有三个点才能使船体的交叉点具有非零区域。该代码计算满足此标准的分区数减去交叉区域为零的分区数。 (P2是2的幂。C是二项式系数。)

当且仅当有一条线分隔两个船体(Hyperplane separation theorem)时,两个凸包的交点为零。我认为我们需要扩展这个结果,实际上,(在正确的假设下)有两条线将船体分开并触碰它们。

最后一个循环计算minuend。计算减数的前一个是几何考虑因素。代码在所有点对上循环,并且考虑到它们的直线,通过有符号区域测试计算每一侧的点数。它增加了从每一侧选择两个或多个点的方法的数量,从而确保,如果我们在一个船体中包括该对中的第一个点而在另一个船体中包括该对中的第二个点,我们得到两个船体由线支持并由线分隔。

我不知道这段代码如何处理退化输入(两个重复点,三个共线点)。