目录
显示
题目链接
https://www.luogu.com.cn/problem/P4460
题解
因为 \(n \leq 20\),考虑状压。
先预处理从 \(i\) 号点到 \(j\) 号点一定经过的点,压到一个集合 \(S_{i,j}\) 中。
设 \(f_{i,j}\) 表示图案经过的点集为 \(i\),且最后一个点是 \(j\) 的方案数。
每次枚举下一个要经过的点,当且仅当这个点之前没被经过,且当前点合下一个点之间的所有点都被经过时才能转移。
最后统计所有经过至少四个点的状态即可。
时间复杂度 \(O(n^2 2^n)\),当然实际合法的状态转移不会达到这个上限。
#include <iostream>
#include <algorithm>
#define MOD 100000007
#define INF 1e9
#define eqs 1e-7
using namespace std;
struct point
{
int x,y;
}p[25];
int f[2500005][25],ans;
int g[25][25];
bool cmp(const point&a,const point&b)
{
return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
double slope(int x1,int y1,int x2,int y2)
{
if(x1==x2)return INF;
return 1.0*(y1-y2)/(x1-x2);
}
int popcount(int x)
{
int ans=0;
while(x)
{
ans++;
x&=(x-1);
}
return ans;
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>p[i].x>>p[i].y;
sort(p+1,p+n+1,cmp);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
for(int k=i+1;k<=j-1;k++)
if(abs(slope(p[i].x,p[i].y,p[k].x,p[k].y)-slope(p[k].x,p[k].y,p[j].x,p[j].y))<=eqs)
g[i][j]|=(1<<k),g[j][i]|=(1<<k);
for(int i=1;i<=n;i++)
f[1<<i][i]=1;
for(int i=0;i<(1<<(n+1));i++)
for(int j=1;j<=n;j++)
if(i&(1<<j))
for(int k=1;k<=n;k++)
if((i&(1<<k))==0&&k!=j&&(i&g[j][k])==g[j][k])
f[i|(1<<k)][k]=(f[i|(1<<k)][k]+f[i][j])%MOD;
for(int i=0;i<(1<<(n+1));i++)
if(popcount(i)>=4)
for(int j=1;j<=n;j++)
if(i&(1<<j))ans=(ans+f[i][j])%MOD;
cout<<ans<<endl;
return 0;
}